@gitkraken/core-gitlens 0.9.3 → 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 (160) hide show
  1. package/CHANGELOG.md +34 -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 +13 -4
  27. package/dist/plus/integrations/integrationService.js.map +1 -1
  28. package/dist/plus/integrations/manager.d.ts +62 -7
  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/issueReads.d.ts +14 -0
  36. package/dist/plus/integrations/models/issuesIntegration.d.ts +44 -3
  37. package/dist/plus/integrations/models/issuesIntegration.js +51 -1
  38. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  39. package/dist/plus/integrations/providers/azure/azure.js +3 -3
  40. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  41. package/dist/plus/integrations/providers/azure/models.d.ts +36 -4
  42. package/dist/plus/integrations/providers/azure/models.js +118 -8
  43. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  44. package/dist/plus/integrations/providers/azureDevOps.d.ts +102 -5
  45. package/dist/plus/integrations/providers/azureDevOps.js +264 -45
  46. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  47. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +30 -6
  48. package/dist/plus/integrations/providers/azureDevOpsServer.js +82 -21
  49. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  50. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +21 -1
  51. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +74 -10
  52. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
  53. package/dist/plus/integrations/providers/bitbucket/models.d.ts +45 -8
  54. package/dist/plus/integrations/providers/bitbucket/models.js +32 -9
  55. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
  56. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +2 -6
  57. package/dist/plus/integrations/providers/bitbucket-server/models.js +1 -3
  58. package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
  59. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +3 -1
  60. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +4 -1
  61. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -1
  62. package/dist/plus/integrations/providers/bitbucket-server.d.ts +2 -2
  63. package/dist/plus/integrations/providers/bitbucket-server.js +7 -5
  64. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  65. package/dist/plus/integrations/providers/bitbucket.d.ts +22 -2
  66. package/dist/plus/integrations/providers/bitbucket.js +29 -6
  67. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  68. package/dist/plus/integrations/providers/github.d.ts +40 -3
  69. package/dist/plus/integrations/providers/github.js +133 -11
  70. package/dist/plus/integrations/providers/github.js.map +1 -1
  71. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +34 -1
  72. package/dist/plus/integrations/providers/gitlab/gitlab.js +118 -4
  73. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
  74. package/dist/plus/integrations/providers/gitlab/models.d.ts +70 -5
  75. package/dist/plus/integrations/providers/gitlab/models.js +106 -13
  76. package/dist/plus/integrations/providers/gitlab/models.js.map +1 -1
  77. package/dist/plus/integrations/providers/gitlab.d.ts +31 -2
  78. package/dist/plus/integrations/providers/gitlab.js +48 -8
  79. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  80. package/dist/plus/integrations/providers/jira-server.d.ts +3 -2
  81. package/dist/plus/integrations/providers/jira-server.js +18 -9
  82. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  83. package/dist/plus/integrations/providers/jira.d.ts +26 -2
  84. package/dist/plus/integrations/providers/jira.js +120 -9
  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 +105 -7
  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 +70 -8
  96. package/dist/plus/integrations/providers/models.js +59 -21
  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 +70 -4
  101. package/dist/plus/integrations/providers/providersApi.js +148 -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 +11 -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/cursors.d.ts +8 -0
  120. package/dist/plus/integrations/reads/cursors.js +5 -3
  121. package/dist/plus/integrations/reads/cursors.js.map +1 -1
  122. package/dist/plus/integrations/reads/drains.d.ts +13 -4
  123. package/dist/plus/integrations/reads/drains.js +29 -5
  124. package/dist/plus/integrations/reads/drains.js.map +1 -1
  125. package/dist/plus/integrations/reads/etag.d.ts +26 -0
  126. package/dist/plus/integrations/reads/etag.js +108 -0
  127. package/dist/plus/integrations/reads/etag.js.map +1 -0
  128. package/dist/plus/integrations/reads/filters.d.ts +8 -0
  129. package/dist/plus/integrations/reads/filters.js +2 -1
  130. package/dist/plus/integrations/reads/filters.js.map +1 -1
  131. package/dist/plus/integrations/reads/hierarchy.js +3 -3
  132. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  133. package/dist/plus/integrations/reads/issueBatch.d.ts +47 -16
  134. package/dist/plus/integrations/reads/issueBatch.js +55 -87
  135. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  136. package/dist/plus/integrations/reads/issueTracker.d.ts +10 -2
  137. package/dist/plus/integrations/reads/issueTracker.js +23 -6
  138. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  139. package/dist/plus/integrations/reads/issues.d.ts +1 -1
  140. package/dist/plus/integrations/reads/issues.js +4 -4
  141. package/dist/plus/integrations/reads/issues.js.map +1 -1
  142. package/dist/plus/integrations/reads/pullRequestBatch.d.ts +44 -6
  143. package/dist/plus/integrations/reads/pullRequestBatch.js +31 -34
  144. package/dist/plus/integrations/reads/pullRequestBatch.js.map +1 -1
  145. package/dist/plus/integrations/reads/pullRequestBranches.d.ts +5 -2
  146. package/dist/plus/integrations/reads/pullRequestBranches.js +4 -3
  147. package/dist/plus/integrations/reads/pullRequestBranches.js.map +1 -1
  148. package/dist/plus/integrations/reads/pullRequests.js +8 -4
  149. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  150. package/dist/plus/integrations/reads/sweeps.js +8 -4
  151. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  152. package/dist/plus/integrations/reads/warnings.d.ts +23 -8
  153. package/dist/plus/integrations/reads/warnings.js +43 -19
  154. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  155. package/dist/plus/integrations/results.d.ts +12 -4
  156. package/dist/plus/integrations/results.js +2 -0
  157. package/dist/plus/integrations/results.js.map +1 -1
  158. package/docs/integrations.md +389 -23
  159. package/docs/kepler-read-api-parity.md +16 -2
  160. package/package.json +2 -2
@@ -2,9 +2,9 @@ import { Disposable } from "../../../utils/disposable.js";
2
2
  import { Emitter, Event } from "../../../utils/event.js";
3
3
  import { ResourceDescriptor } from "../../../git/models/resourceDescriptor.js";
4
4
  import { AutolinkReference, DynamicAutolinkReference } from "../../../git/models/autolink.js";
5
- import { IssueOrPullRequest, IssueOrPullRequestType } from "../../../git/models/issueOrPullRequest.js";
5
+ import { IssueOrPullRequest, IssueOrPullRequestState, IssueOrPullRequestType } from "../../../git/models/issueOrPullRequest.js";
6
6
  import { Issue as Issue$1, IssueShape } from "../../../git/models/issue.js";
7
- import { PullRequest } from "../../../git/models/pullRequest.js";
7
+ import { PullRequest, PullRequestMergeableState, PullRequestReviewDecision, PullRequestReviewState, PullRequestStatusCheckRollupState } from "../../../git/models/pullRequest.js";
8
8
  import { ScopedLogger } from "../../../utils/logger.scoped.js";
9
9
  import { Account as Account$1 } from "../../../git/models/author.js";
10
10
  import { ProviderAuthenticationSession } from "../authentication/models.js";
@@ -44,6 +44,75 @@ export type IntegrationResult<T> = {
44
44
  export type BatchSlot<T> = PromiseFulfilledResult<T> | (PromiseRejectedResult & {
45
45
  failure?: ProviderScopeFailure;
46
46
  });
47
+ /**
48
+ * An input a pull request's etag can be widened to, each of which a host changes without moving `updatedAt`. Every
49
+ * entry costs its own fields in the cheap check, so a caller picks only the ones it needs. `'checks'` is the check
50
+ * rollup.
51
+ */
52
+ export type PullRequestEtagInclude = 'mergeable' | 'reviewDecision' | 'checks';
53
+ /** Every {@link PullRequestEtagInclude}, in the canonical order an etag lists them in. */
54
+ export declare const pullRequestEtagIncludes: readonly PullRequestEtagInclude[];
55
+ /**
56
+ * The change state a batch read's etag is computed from (see `reads/etag.ts`), in the NORMALIZED vocabulary of
57
+ * `PullRequestShape` — the values a full row carries after its provider's whole conversion chain, so a cheap
58
+ * check and a full read of the same pull request yield the same fields. Each of `mergeableState`, `reviewDecision`
59
+ * and `statusCheckRollupState` counts only when its {@link PullRequestEtagInclude} is requested.
60
+ */
61
+ export interface PullRequestEtagFields {
62
+ state: IssueOrPullRequestState;
63
+ isDraft?: boolean;
64
+ updatedDate: Date;
65
+ headSha?: string;
66
+ mergeableState?: PullRequestMergeableState;
67
+ reviewDecision?: PullRequestReviewDecision;
68
+ statusCheckRollupState?: PullRequestStatusCheckRollupState;
69
+ /**
70
+ * A fingerprint of the fields a host changes without moving an update time ({@link pullRequestRevision}); set
71
+ * only for hosts that keep none (Azure DevOps and Server). Part of the etag whenever it is set, not an include.
72
+ */
73
+ revision?: string;
74
+ }
75
+ /**
76
+ * The fields of a pull request {@link pullRequestRevision} reads, as a full row carries them. A `PullRequestShape`
77
+ * is one, so a full read passes its row as is and a cheap check builds one from the same mapped values.
78
+ */
79
+ export interface PullRequestRevisionSource {
80
+ readonly title: string;
81
+ readonly body?: string;
82
+ readonly refs?: {
83
+ readonly base: {
84
+ readonly branch: string;
85
+ };
86
+ };
87
+ readonly reviewRequests?: readonly PullRequestRevisionReviewer[];
88
+ readonly latestReviews?: readonly PullRequestRevisionReviewer[];
89
+ }
90
+ interface PullRequestRevisionReviewer {
91
+ readonly reviewer: {
92
+ readonly id: string;
93
+ };
94
+ readonly state: PullRequestReviewState;
95
+ }
96
+ /**
97
+ * {@link PullRequestEtagFields.revision}: a 64-bit hash of a pull request's title, description, target branch and
98
+ * reviewers (each one's id and review state, in a canonical order). Hashed to keep the etag short, so a collision is
99
+ * the only way a change can hide, at about 2^-64.
100
+ */
101
+ export declare function pullRequestRevision(pr: PullRequestRevisionSource): string;
102
+ /**
103
+ * An input an issue's etag can be widened to, which a host changes without moving `updatedAt`. `'reactions'` is the
104
+ * thumbs-up count, and widens the etag only on hosts whose rows really fetch one (GitHub/GHE and GitLab).
105
+ */
106
+ export type IssueEtagInclude = 'reactions';
107
+ /** Every {@link IssueEtagInclude}, in the canonical order an etag lists them in. */
108
+ export declare const issueEtagIncludes: readonly IssueEtagInclude[];
109
+ /** The issue twin of {@link PullRequestEtagFields}, in {@link IssueShape}'s normalized vocabulary. */
110
+ export interface IssueEtagFields {
111
+ state: IssueOrPullRequestState;
112
+ updatedDate: Date;
113
+ /** Counts only when `'reactions'` is requested, and only from a row whose read fetched reactions. */
114
+ thumbsUpCount?: number;
115
+ }
47
116
  /** A batch read's target, in the form its provider addresses it by: a repository coordinate, or a tracker's resource. */
48
117
  type BatchTarget = {
49
118
  owner: string;
@@ -52,7 +121,7 @@ type BatchTarget = {
52
121
  } | {
53
122
  resourceId: string;
54
123
  };
55
- type SyncReqUsecase = Exclude<'getAccountForCommit' | 'getAccountForEmail' | 'getAccountForResource' | 'getCurrentAccount' | 'getDefaultBranch' | 'getIssue' | 'getIssueOrPullRequest' | 'getIssuesBatch' | 'getIssuesForProject' | 'getIssuesForRepos' | 'getMyPullRequestsForUser' | 'getOrganizationsForUser' | 'getProjectsForOrg' | 'getProjectsForResources' | 'getPullRequest' | 'getPullRequestsBatch' | 'getPullRequestsForBranches' | 'getRepositoriesForOrg' | 'getRepositoriesForUser' | 'getPullRequestForBranch' | 'getPullRequestForCommit' | 'getPullRequestsForRepos' | 'getRepositoryMetadata' | 'getResourcesForUser' | 'getSshSigningKeysForEmails' | 'countIssues' | 'countPullRequests' | 'mergePullRequest' | 'searchIssuesPage' | 'searchPullRequestsPage' | 'searchMyIssues' | 'searchMyPullRequests' | 'searchPullRequests', 'all'>;
124
+ type SyncReqUsecase = Exclude<'getAccountForCommit' | 'getAccountForEmail' | 'getAccountForResource' | 'getCurrentAccount' | 'getDefaultBranch' | 'getIssue' | 'getIssueOrPullRequest' | 'getIssuesBatch' | 'getIssuesEtagFields' | 'getIssuesForProject' | 'getIssuesForRepos' | 'getMyPullRequestsForUser' | 'getOrganizationsForUser' | 'getProjectsForOrg' | 'getProjectsForResources' | 'getPullRequest' | 'getPullRequestsBatch' | 'getPullRequestsEtagFields' | 'getPullRequestsForBranches' | 'getRepositoriesForOrg' | 'getRepositoriesForUser' | 'getPullRequestForBranch' | 'getPullRequestForCommit' | 'getPullRequestsForRepos' | 'getRepositoryMetadata' | 'getResourcesForUser' | 'getSshSigningKeysForEmails' | 'countIssues' | 'countPullRequests' | 'mergePullRequest' | 'searchIssuesPage' | 'searchPullRequestsPage' | 'searchMyIssues' | 'searchMyPullRequests' | 'searchPullRequests', 'all'>;
56
125
  export declare abstract class IntegrationBase<ID extends IntegrationIds = IntegrationIds, T extends ResourceDescriptor = ResourceDescriptor> implements Disposable {
57
126
  protected readonly ctx: IntegrationServiceContext;
58
127
  protected readonly authenticationService: IntegrationAuthenticationService;
@@ -223,8 +292,15 @@ export declare abstract class IntegrationBase<ID extends IntegrationIds = Integr
223
292
  * with {@link validateCredential}. A check that proves nothing leaves the refusals scoped and unnamed, as it leaves
224
293
  * an account-wide read's. A provider with no check cannot tell, and a refusal it pins on the credential (see
225
294
  * {@link isCredentialRefusal}) needs none, so either still fails the call.
295
+ *
296
+ * `keepOtherFailures` is for the etag cheap check, whose failed targets fall through to a full read one by one:
297
+ * there, only a refused credential fails the call, always on one of the refusals, and every other failure stays
298
+ * its own slot's. A batch with refusals and other failures but no answer then has its credential checked, as when
299
+ * every target refused, and a provider with no check leaves its refusals scoped and unnamed.
226
300
  */
227
- protected settleBatchRefusals<T>(session: ProviderAuthenticationSession, targets: readonly BatchTarget[], slots: PromiseSettledResult<T>[]): Promise<BatchSlot<T>[]>;
301
+ protected settleBatchRefusals<T>(session: ProviderAuthenticationSession, targets: readonly BatchTarget[], slots: PromiseSettledResult<T>[], options?: {
302
+ keepOtherFailures?: boolean;
303
+ }): Promise<BatchSlot<T>[]>;
228
304
  /**
229
305
  * Shows one notice, and spends at most one strike, for a batch read whose targets failed with a server error or a
230
306
  * timeout. Our own clients leave both to the batch (see `reportRequestFailure`) instead of spending them per
@@ -232,6 +308,17 @@ export declare abstract class IntegrationBase<ID extends IntegrationIds = Integr
232
308
  * answered some targets showed the host is up, and budgets strikes the same way as the rest of its failures.
233
309
  */
234
310
  protected reportDeferredRequestFailures(slots: readonly PromiseSettledResult<unknown>[] | undefined, strike: boolean): void;
311
+ /**
312
+ * The failure handling of an etag cheap check that failed as a whole. Only a refused credential runs
313
+ * {@link handleProviderException}, for its session expiry, re-authentication and strike. Anything else is only
314
+ * logged, spending no strike and showing no notice: every target the check held then falls through to a full
315
+ * read, which spends and notifies if the host really is failing, or is dropped, as rate-limited or without a
316
+ * connection, with the check's warning. A rate limit costs nothing in `handleProviderException` either.
317
+ */
318
+ protected handleEtagCheckException(syncReqUsecase: SyncReqUsecase, ex: Error, options: {
319
+ scope: ScopedLogger | undefined;
320
+ connectionId: string | undefined;
321
+ }): void;
235
322
  /** Marks the scoped refusals the provider pins on the credential (see {@link isCredentialRefusal}). */
236
323
  private markCredentialRefusals;
237
324
  /**
@@ -19,6 +19,32 @@ import { providersMetadata } from "../providers/models.js";
19
19
  import { RejectedTokenTracker } from "../authentication/rejectedTokenTracker.js";
20
20
  import { getCachedIssue } from "./issueCache.js";
21
21
  //#region ../plus/integrations/src/models/integration.ts
22
+ /** Every {@link PullRequestEtagInclude}, in the canonical order an etag lists them in. */
23
+ const pullRequestEtagIncludes = [
24
+ "mergeable",
25
+ "reviewDecision",
26
+ "checks"
27
+ ];
28
+ /**
29
+ * {@link PullRequestEtagFields.revision}: a 64-bit hash of a pull request's title, description, target branch and
30
+ * reviewers (each one's id and review state, in a canonical order). Hashed to keep the etag short, so a collision is
31
+ * the only way a change can hide, at about 2^-64.
32
+ */
33
+ function pullRequestRevision(pr) {
34
+ return fnv1aHash64(JSON.stringify([
35
+ pr.title,
36
+ pr.body ?? null,
37
+ pr.refs?.base.branch ?? null,
38
+ toRevisionReviewers(pr.reviewRequests),
39
+ toRevisionReviewers(pr.latestReviews)
40
+ ]));
41
+ }
42
+ /** Each reviewer as its serialized `[id, state]`, sorted, so the host's order never moves the revision. */
43
+ function toRevisionReviewers(reviewers) {
44
+ return reviewers?.map((r) => JSON.stringify([r.reviewer.id, r.state])).sort() ?? null;
45
+ }
46
+ /** Every {@link IssueEtagInclude}, in the canonical order an etag lists them in. */
47
+ const issueEtagIncludes = ["reactions"];
22
48
  /**
23
49
  * The scope a batch read records a refused target under. It is decided here rather than in the read, which has the
24
50
  * same targets, because naming the refusal's cause (`describeRefusal`) needs it too. Azure DevOps: the organization
@@ -368,12 +394,18 @@ var IntegrationBase = class IntegrationBase {
368
394
  * with {@link validateCredential}. A check that proves nothing leaves the refusals scoped and unnamed, as it leaves
369
395
  * an account-wide read's. A provider with no check cannot tell, and a refusal it pins on the credential (see
370
396
  * {@link isCredentialRefusal}) needs none, so either still fails the call.
397
+ *
398
+ * `keepOtherFailures` is for the etag cheap check, whose failed targets fall through to a full read one by one:
399
+ * there, only a refused credential fails the call, always on one of the refusals, and every other failure stays
400
+ * its own slot's. A batch with refusals and other failures but no answer then has its credential checked, as when
401
+ * every target refused, and a provider with no check leaves its refusals scoped and unnamed.
371
402
  */
372
- async settleBatchRefusals(session, targets, slots) {
403
+ async settleBatchRefusals(session, targets, slots, options) {
404
+ const keepOtherFailures = options?.keepOtherFailures ?? false;
373
405
  const failures = slots.map((slot, i) => slot.status === "rejected" && slot.reason instanceof AuthenticationError ? toCollectionScopeFailure(batchTargetScope(this.id, targets[i]), slot.reason) : void 0);
374
406
  const refused = failures.filter((f) => f != null);
375
407
  if (!refused.length) {
376
- throwIfAllSettledFailed(slots);
408
+ if (!keepOtherFailures) throwIfAllSettledFailed(slots);
377
409
  return slots;
378
410
  }
379
411
  const metadata = {
@@ -382,12 +414,13 @@ var IntegrationBase = class IntegrationBase {
382
414
  };
383
415
  const credentialRefused = this.markCredentialRefusals(metadata);
384
416
  if (slots.some((slot) => slot.status === "fulfilled")) this.nameRefusalCauses(session, metadata);
385
- else if (credentialRefused) throwIfAllSettledFailed(slots);
386
- else if (refused.length < slots.length) throwIfAllSettledFailed(slots.filter((_, i) => failures[i] == null));
417
+ else if (credentialRefused) throwIfAllSettledFailed(keepOtherFailures ? slots.filter((_, i) => failures[i] != null) : slots);
418
+ else if (refused.length < slots.length && !keepOtherFailures) throwIfAllSettledFailed(slots.filter((_, i) => failures[i] == null));
387
419
  else {
388
420
  const confirmation = await this.confirmCredential(session);
389
- if (confirmation == null) throwIfAllSettledFailed(slots);
390
- else if (confirmation === "confirmed") this.nameRefusalCauses(session, metadata);
421
+ if (confirmation == null) {
422
+ if (refused.length === slots.length) throwIfAllSettledFailed(slots);
423
+ } else if (confirmation === "confirmed") this.nameRefusalCauses(session, metadata);
391
424
  }
392
425
  return slots.map((slot, i) => {
393
426
  const failure = failures[i];
@@ -414,6 +447,22 @@ var IntegrationBase = class IntegrationBase {
414
447
  if (strike) this.trackRequestException();
415
448
  notify();
416
449
  }
450
+ /**
451
+ * The failure handling of an etag cheap check that failed as a whole. Only a refused credential runs
452
+ * {@link handleProviderException}, for its session expiry, re-authentication and strike. Anything else is only
453
+ * logged, spending no strike and showing no notice: every target the check held then falls through to a full
454
+ * read, which spends and notifies if the host really is failing, or is dropped, as rate-limited or without a
455
+ * connection, with the check's warning. A rate limit costs nothing in `handleProviderException` either.
456
+ */
457
+ handleEtagCheckException(syncReqUsecase, ex, options) {
458
+ if (ex instanceof AuthenticationError) {
459
+ this.handleProviderException(syncReqUsecase, ex, options);
460
+ return;
461
+ }
462
+ if (isCancellationError(ex)) return;
463
+ if (options.scope != null) options.scope.error(ex);
464
+ else _logger.error(ex);
465
+ }
417
466
  /** Marks the scoped refusals the provider pins on the credential (see {@link isCredentialRefusal}). */
418
467
  markCredentialRefusals(metadata) {
419
468
  const isCredentialRefusal = this.isCredentialRefusal?.bind(this);
@@ -745,6 +794,6 @@ __decorate([trace()], IntegrationBase.prototype, "getLinkedIssueOrPullRequest",
745
794
  __decorate([trace()], IntegrationBase.prototype, "getIssueResultCore", null);
746
795
  __decorate([trace()], IntegrationBase.prototype, "getPullRequest", null);
747
796
  //#endregion
748
- export { IntegrationBase };
797
+ export { IntegrationBase, issueEtagIncludes, pullRequestEtagIncludes, pullRequestRevision };
749
798
 
750
799
  //# sourceMappingURL=integration.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":[],"sources":["../../../../../plus/integrations/src/models/integration.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScope } from '@gitkraken/provider-apis';\nimport { getDeferredRequestFailure } from '@gitlens/git/errors.js';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '@gitlens/git/models/autolink.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestType } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequest } from '@gitlens/git/models/pullRequest.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug, trace } from '@gitlens/utils/decorators/log.js';\nimport type { Disposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { fnv1aHash64 } from '@gitlens/utils/hash.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type {\n\tIntegrationAuthenticationProviderDescriptor,\n\tIntegrationAuthenticationSessionDescriptor,\n} from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { RejectedTokenTracker } from '../authentication/rejectedTokenTracker.js';\nimport type { ProviderRefusal, ProviderScopeFailure } from '../collectionMetadata.js';\nimport {\n\tattributeScopedAuthFailures,\n\thasOnlyScopedAuthFailures,\n\tmarkCredentialRefusals,\n\ttoCollectionScopeFailure,\n} from '../collectionMetadata.js';\nimport type { IntegrationIds, IssuesCloudHostIntegrationId, IssuesHostIntegrationIds } from '../constants.js';\nimport { GitCloudHostIntegrationId } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport { AuthenticationError, AuthenticationErrorReason, RequestClientError, toError } from '../errors.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport { providersMetadata } from '../providers/models.js';\nimport { isAzureProviderId } from '../providers/providerErrors.js';\nimport type { ProvidersApi } from '../providers/providersApi.js';\nimport { throwIfAllSettledFailed } from '../providers/utils/providerPaging.js';\nimport type { ProviderWarningCause, ProviderWarningScope } from '../results.js';\nimport type { Sources } from '../telemetry.js';\nimport { areDomainsOnSameHost } from '../utils/domain.utils.js';\nimport { isSelfManagedHostIntegrationId } from '../utils/integration.utils.js';\nimport type { GitHostIntegration } from './gitHostIntegration.js';\nimport { getCachedIssue } from './issueCache.js';\nimport type { AccountWideIssuesResult, SearchMyIssuesOptions } from './issueReads.js';\nimport type { IssuesIntegration } from './issuesIntegration.js';\n\nexport type Integration = GitHostIntegration | IssuesIntegration;\nexport type IntegrationById<T extends IntegrationIds> = T extends IssuesHostIntegrationIds\n\t? IssuesIntegration\n\t: GitHostIntegration;\nexport type IntegrationType = 'git' | 'issues';\n\n// The issue-read contracts live in their own module (pure data, and their relationship to each other is the\n// point of reading them together); re-exported here so the providers that implement these reads keep one import.\nexport type {\n\tAccountWideIssuesResult,\n\tProviderIssueSearchPage,\n\tProviderSearchCount,\n\tSearchMyIssuesOptions,\n} from './issueReads.js';\nexport type { ProviderPullRequestCount, ProviderPullRequestSearchPage } from './pullRequestReads.js';\n\n// Keep this in step with `isSelfManagedHostIntegrationId`: it is the type-level twin of that predicate, and a\n// disagreement compiles cleanly while the runtime writes a domain-keyed key the type says is unkeyed (so the\n// local-disconnect flag is written under one key and read under another).\nexport type IntegrationKey<T extends IntegrationIds = IntegrationIds> = T extends\n\t| GitCloudHostIntegrationId\n\t| IssuesCloudHostIntegrationId\n\t? `${T}`\n\t: `${T}:${string}`;\n\nexport type IntegrationConnectedKey<T extends IntegrationIds = IntegrationIds> = `connected:${IntegrationKey<T>}`;\n\nexport type IntegrationResult<T> =\n\t| { value: T; duration?: number; error?: Error }\n\t| { error: Error; duration?: number; value?: never }\n\t| undefined;\n\n/**\n * One target's answer in a batch read. A slot rejected by an authentication refusal carries the scope failure it was\n * recorded as (see `IntegrationBase.settleBatchRefusals`).\n */\nexport type BatchSlot<T> = PromiseFulfilledResult<T> | (PromiseRejectedResult & { failure?: ProviderScopeFailure });\n\n/** A batch read's target, in the form its provider addresses it by: a repository coordinate, or a tracker's resource. */\ntype BatchTarget = { owner: string; repo: string; project?: string } | { resourceId: string };\n\n/**\n * The scope a batch read records a refused target under. It is decided here rather than in the read, which has the\n * same targets, because naming the refusal's cause (`describeRefusal`) needs it too. Azure DevOps: the organization\n * and project, since its refusals come from those (an organization's OAuth policy or tenant, a project's permissions)\n * and its work items belong to no repository. Every other git host: the repository, `owner/repo`, which is how its\n * repo-scoped reads record one. A tracker: the resource.\n */\nfunction batchTargetScope(providerId: IntegrationIds, target: BatchTarget): CollectionScope {\n\tif ('resourceId' in target) return { providerId: providerId, resourceId: target.resourceId };\n\tif (isAzureProviderId(providerId)) {\n\t\treturn { providerId: providerId, resourceId: target.owner, projectId: target.project };\n\t}\n\treturn { providerId: providerId, repositoryId: `${target.owner}/${target.repo}` };\n}\n\ntype SyncReqUsecase = Exclude<\n\t| 'getAccountForCommit'\n\t| 'getAccountForEmail'\n\t| 'getAccountForResource'\n\t| 'getCurrentAccount'\n\t| 'getDefaultBranch'\n\t| 'getIssue'\n\t| 'getIssueOrPullRequest'\n\t| 'getIssuesBatch'\n\t| 'getIssuesForProject'\n\t| 'getIssuesForRepos'\n\t| 'getMyPullRequestsForUser'\n\t| 'getOrganizationsForUser'\n\t| 'getProjectsForOrg'\n\t| 'getProjectsForResources'\n\t| 'getPullRequest'\n\t| 'getPullRequestsBatch'\n\t| 'getPullRequestsForBranches'\n\t| 'getRepositoriesForOrg'\n\t| 'getRepositoriesForUser'\n\t| 'getPullRequestForBranch'\n\t| 'getPullRequestForCommit'\n\t| 'getPullRequestsForRepos'\n\t| 'getRepositoryMetadata'\n\t| 'getResourcesForUser'\n\t| 'getSshSigningKeysForEmails'\n\t| 'countIssues'\n\t| 'countPullRequests'\n\t| 'mergePullRequest'\n\t| 'searchIssuesPage'\n\t| 'searchPullRequestsPage'\n\t| 'searchMyIssues'\n\t| 'searchMyPullRequests'\n\t| 'searchPullRequests',\n\t// excluding to show explicitly that we don't want to add 'all' key occasionally\n\t'all'\n>;\n\nexport abstract class IntegrationBase<\n\tID extends IntegrationIds = IntegrationIds,\n\tT extends ResourceDescriptor = ResourceDescriptor,\n> implements Disposable {\n\tabstract readonly type: IntegrationType;\n\n\tprivate readonly _onDidChange = new Emitter<void>();\n\tget onDidChange(): Event<void> {\n\t\treturn this._onDidChange.event;\n\t}\n\n\tconstructor(\n\t\tprotected readonly ctx: IntegrationServiceContext,\n\t\tprotected readonly authenticationService: IntegrationAuthenticationService,\n\t\tprotected readonly getProvidersApi: () => Promise<ProvidersApi>,\n\t\tprivate readonly didChangeConnection: Emitter<IntegrationConnectionChangeEvent>,\n\t) {}\n\n\tdispose(): void {\n\t\tthis._onDidChange.dispose();\n\t}\n\n\tabstract get authProvider(): IntegrationAuthenticationProviderDescriptor;\n\tabstract get id(): ID;\n\tprotected abstract get key(): IntegrationKey<ID>;\n\tabstract get name(): string;\n\tabstract get domain(): string;\n\n\tget authProviderDescriptor(): IntegrationAuthenticationSessionDescriptor {\n\t\treturn { domain: this.domain, scopes: this.authProvider.scopes };\n\t}\n\n\t/**\n\t * The `gl-provider-<key>` glicon key for this provider, which is NOT always its id.\n\t *\n\t * `providersMetadata.iconKey` is the field that says which glyph a provider draws with, and a\n\t * self-managed variant reuses its cloud family's — Jira Data Center draws with Jira's, because it is the\n\t * same product and the id only selects an icon. Returning the id instead named a glyph the registry has\n\t * no entry for, so a consumer rendering a `gl-provider-` glicon from `issue.provider.icon` requested a\n\t * glyph that does not exist.\n\t */\n\tget icon(): string {\n\t\treturn providersMetadata[this.id]?.iconKey ?? this.id;\n\t}\n\n\taccess(): Promise<boolean> {\n\t\treturn this.ctx.account.isTrialOrPaid();\n\t}\n\n\tautolinks():\n\t\t| (AutolinkReference | DynamicAutolinkReference)[]\n\t\t| Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\treturn [];\n\t}\n\n\tprivate get connectedKey(): IntegrationConnectedKey<ID> {\n\t\treturn `connected:${this.key}`;\n\t}\n\n\tget maybeConnected(): boolean | undefined {\n\t\treturn this._session === undefined ? undefined : this._session !== null;\n\t}\n\n\t/** Hash of the current session's access token. Changes on any token change (account switch or refresh). */\n\tprivate _sessionFingerprint: { session: ProviderAuthenticationSession; hash: string } | undefined;\n\tget sessionFingerprint(): string | undefined {\n\t\tif (this._session == null) return undefined;\n\n\t\tif (this._sessionFingerprint?.session !== this._session) {\n\t\t\tthis._sessionFingerprint = { session: this._session, hash: this.getSessionFingerprint(this._session) };\n\t\t}\n\t\treturn this._sessionFingerprint.hash;\n\t}\n\n\tprotected getSessionFingerprint(session: ProviderAuthenticationSession): string {\n\t\treturn fnv1aHash64(session.accessToken);\n\t}\n\n\tget connectionExpired(): boolean | undefined {\n\t\tif (this._session?.expiresAt == null) return undefined;\n\t\treturn new Date(this._session.expiresAt) < new Date();\n\t}\n\n\tprotected _session: ProviderAuthenticationSession | null | undefined;\n\tgetSession(\n\t\tsource: Sources,\n\t): ProviderAuthenticationSession | Promise<ProviderAuthenticationSession | undefined> | undefined {\n\t\tif (this._session === undefined) {\n\t\t\treturn this.ensureSession({ createIfNeeded: false, source: source });\n\t\t}\n\t\treturn this._session != null && this.isSessionForIntegrationHost(this._session) ? this._session : undefined;\n\t}\n\n\tprivate isSessionForIntegrationHost(session: ProviderAuthenticationSession): boolean {\n\t\tif (!isSelfManagedHostIntegrationId(this.id)) return true;\n\n\t\treturn areDomainsOnSameHost(this.domain, session.domain);\n\t}\n\n\t/**\n\t * Resolves the session to read as, for a per-connection (multi-account) read. When `connectionId` is\n\t * omitted this is the integration's primary session, resolved exactly like the existing read flow\n\t * (ensure-connected + refresh-if-expired). When set, it resolves THAT connection's session directly\n\t * from the auth provider — refreshing it if expired — WITHOUT disturbing the cached primary\n\t * `_session`. Returns undefined when the requested session can't be resolved (e.g. the connection is\n\t * gone or the provider isn't connected), so callers degrade to \"no results\".\n\t */\n\tprotected async resolveReadSession(\n\t\tconnectionId: string | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tsource?: Sources,\n\t): Promise<ProviderAuthenticationSession | undefined> {\n\t\tif (\n\t\t\tthis.ctx.config.isIntegrationsEnabled?.() === false ||\n\t\t\tthis.ctx.storage.getWorkspace(this.connectedKey) === false\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// A truthy connectionId targets a specific account; an empty string is not a real target, so it falls\n\t\t// through to the primary path below.\n\t\tif (connectionId) {\n\t\t\t// A read of this connection previously failed with an AuthenticationError, so the token in\n\t\t\t// storage is known-refused. Ask for a refresh through the GK cloud before reading again, which\n\t\t\t// is this branch's equivalent of the primary path's `refreshSessionIfExpired`. Claimed here so\n\t\t\t// the forced refresh runs at most once per rejection.\n\t\t\tconst refreshRejectedToken = this._rejectedTokens.claimRefresh(connectionId);\n\t\t\t// Degrade to \"no results\" on failure, matching the primary path (whose ensureSession/\n\t\t\t// refreshSessionIfExpired swallow errors) so read methods keep their never-throws contract.\n\t\t\ttry {\n\t\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\t\tconst session = await authProvider.getSession(\n\t\t\t\t\t{ ...this.authProviderDescriptor, connectionId: connectionId, cloud: true },\n\t\t\t\t\t{ source: source, refreshRejectedToken: refreshRejectedToken },\n\t\t\t\t);\n\t\t\t\treturn session != null && this.isSessionForIntegrationHost(session) ? session : undefined;\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected) return undefined;\n\n\t\tawait this.refreshSessionIfExpired(scope);\n\t\treturn this._session != null && this.isSessionForIntegrationHost(this._session) ? this._session : undefined;\n\t}\n\n\t@debug()\n\tasync connect(source: Sources): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Boolean(await this.ensureSession({ createIfNeeded: true, source: source }));\n\t\t} catch (_ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected providerOnConnect?(): void | Promise<void>;\n\n\t@gate()\n\t@debug()\n\tasync disconnect(options?: { silent?: boolean; currentSessionOnly?: boolean }): Promise<void> {\n\t\tif (options?.currentSessionOnly && this._session === null) return;\n\n\t\tconst connected = this._session != null;\n\n\t\tlet signOut = !options?.currentSessionOnly;\n\n\t\tif (connected && !options?.currentSessionOnly && !options?.silent) {\n\t\t\tconst decision = await this.ctx.hooks?.onConfirmDisconnect?.({\n\t\t\t\tintegrationName: this.name,\n\t\t\t\tofferSignOut: this.authenticationService.supports(this.authProvider.id),\n\t\t\t});\n\t\t\tif (decision == null) return;\n\n\t\t\tsignOut = decision.signOut;\n\t\t}\n\n\t\tif (signOut) {\n\t\t\t// Disconnecting a provider signs out of ALL its connected accounts (multi-account), not just the\n\t\t\t// primary — otherwise secondary connections' secrets/config would be orphaned. Removing a single\n\t\t\t// account is done via IntegrationService.deleteConnection instead. Pass this instance's descriptor\n\t\t\t// so self-managed disconnects stay scoped to this host: those group every host under one provider\n\t\t\t// id, so an unscoped clear would sign the user out of unrelated hosts. deleteAllSessions derives an\n\t\t\t// undefined domain for cloud providers, so they still clear every account as intended.\n\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\t// Awaited, not fire-and-forget: a caller that awaits `disconnect()` has to be able to rely on the\n\t\t\t// secrets and descriptors actually being gone when it resumes. Left floating, the only thing that\n\t\t\t// ever made this land in time was incidental scheduling slack — `syncCloudIntegrations` used to\n\t\t\t// await each provider in turn, so a later iteration's suspension let the previous provider's delete\n\t\t\t// finish. Syncing providers concurrently removes that slack and the clear was observably still\n\t\t\t// pending when the sync returned.\n\t\t\tawait authProvider.deleteAllSessions(this.authProviderDescriptor);\n\t\t}\n\n\t\tthis.resetRequestExceptionCount('all');\n\t\tthis._session = null;\n\n\t\tif (connected) {\n\t\t\t// Don't store the disconnected flag if silently disconnecting or disconnecting this only for\n\t\t\t// this current VS Code session (will be re-connected on next restart)\n\t\t\tif (!options?.currentSessionOnly && !options?.silent) {\n\t\t\t\tvoid this.ctx.storage.storeWorkspace(this.connectedKey, false).catch();\n\t\t\t}\n\n\t\t\tthis._onDidChange.fire();\n\t\t\tif (!options?.currentSessionOnly) {\n\t\t\t\tthis.didChangeConnection?.fire({ integration: this, key: this.key, reason: 'disconnected' });\n\t\t\t}\n\t\t}\n\n\t\tawait this.providerOnDisconnect?.();\n\t}\n\n\tprotected providerOnDisconnect?(): void | Promise<void>;\n\n\t@debug()\n\tasync reauthenticate(): Promise<void> {\n\t\t// `forceNewSession` below deletes the stored secrets to reconnect. Ahead of the guard — see\n\t\t// `onStoredTokensReplaced`.\n\t\tthis.onStoredTokensReplaced();\n\n\t\tif (this._session === undefined) return;\n\n\t\tthis._session = undefined;\n\t\tvoid (await this.ensureSession({ createIfNeeded: true, forceNewSession: true }));\n\t}\n\n\trefresh(): void {\n\t\tvoid this.ensureSession({ createIfNeeded: false });\n\t}\n\n\tprivate _syncRequestsPerFailedUsecase = new Set<SyncReqUsecase>();\n\thasSessionSyncRequests(): boolean {\n\t\treturn this._syncRequestsPerFailedUsecase.size > 0;\n\t}\n\trequestSessionSyncForUsecase(syncReqUsecase: SyncReqUsecase): void {\n\t\tthis._syncRequestsPerFailedUsecase.add(syncReqUsecase);\n\t}\n\n\t/**\n\t * The per-connection counterpart of the primary session's expire-and-resync recovery: a token the\n\t * provider refused is refreshed once through the GK cloud, which exchanges the refresh token server-side\n\t * (the client never holds one). Armed by {@link handleProviderException} and consumed by\n\t * {@link resolveReadSession}; a rejection it declines belongs to {@link trackRequestException} instead.\n\t */\n\tprivate readonly _rejectedTokens = new RejectedTokenTracker();\n\n\t/**\n\t * Called by every path that replaces or removes the stored tokens — a disconnect, a forced re-sync, a\n\t * reauthentication, or the connection set changing. A rejection names a specific credential, so once that\n\t * credential is gone the rejection describes nothing and must not force a refresh (or, for a deleted\n\t * connection, outlive it).\n\t *\n\t * Deliberately NOT conditioned on `_session`. A per-connection read resolves through the auth provider and\n\t * never populates the cached primary session, so the connections this recovery exists for are exactly the\n\t * ones with no `_session` to inspect — the same blind spot the recovery itself was added to fix. Callers\n\t * that guard on `_session` therefore invoke this ahead of that guard.\n\t */\n\tprotected onStoredTokensReplaced(): void {\n\t\tthis._rejectedTokens.clear();\n\t\tthis.invalidateDiscoveryCaches();\n\t}\n\n\t/**\n\t * Drops what this integration discovered through its session and kept for later reads: a tracker's sites,\n\t * projects or teams. Called by every path that replaces the stored tokens (see {@link onStoredTokensReplaced}),\n\t * which includes the forced re-sync a consumer's refresh runs, and by a read asked to `forceSync`. A refresh is\n\t * the consumer saying the account may have changed, and a project created or deleted since the first read would\n\t * otherwise stay invisible, or keep being searched, for the rest of the session (#5907).\n\t */\n\tinvalidateDiscoveryCaches(): void {}\n\n\tprivate static readonly requestExceptionLimit = 5;\n\tprivate requestExceptionCount = 0;\n\n\tresetRequestExceptionCount(syncReqUsecase: SyncReqUsecase | 'all'): void {\n\t\tthis.requestExceptionCount = 0;\n\t\tif (syncReqUsecase === 'all') {\n\t\t\tthis._syncRequestsPerFailedUsecase.clear();\n\t\t\t// 'all' is the whole-integration reset: a disconnect, or a re-sync that produced a new access token.\n\t\t\tthis.onStoredTokensReplaced();\n\t\t} else {\n\t\t\tthis._syncRequestsPerFailedUsecase.delete(syncReqUsecase);\n\t\t}\n\t}\n\n\t/**\n\t * Resets request exceptions without resetting the amount of syncs\n\t */\n\tsmoothifyRequestExceptionCount(): void {\n\t\t// On resync we reset exception count only to avoid infinitive syncs on failure\n\t\tthis.requestExceptionCount = 0;\n\t}\n\n\tasync reset(): Promise<void> {\n\t\tawait this.disconnect({ silent: true });\n\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\t}\n\n\t/**\n\t * Drops the in-memory session so the next access re-resolves it from storage. Used when the primary\n\t * connection changed underneath a warm integration (e.g. after `setPrimaryConnection`/\n\t * `deleteConnection`). Unlike {@link reset}/{@link disconnect}, it deletes nothing from storage.\n\t */\n\tswitchConnection(): void {\n\t\t// A connection was deleted, or a different one became primary. Ahead of the guard — see\n\t\t// `onStoredTokensReplaced`.\n\t\tthis.onStoredTokensReplaced();\n\n\t\tif (this._session === undefined) return;\n\n\t\tconst wasConnected = this._session != null;\n\t\tthis._session = undefined;\n\t\tthis._onDidChange.fire();\n\t\tvoid this.refreshAfterSwitch(wasConnected);\n\t}\n\n\tprivate async refreshAfterSwitch(wasConnected: boolean): Promise<void> {\n\t\tconst session = await this.ensureSession({ createIfNeeded: false });\n\t\tif (session != null || !wasConnected) return;\n\n\t\tthis._onDidChange.fire();\n\t\tthis.didChangeConnection?.fire({ integration: this, key: this.key, reason: 'disconnected' });\n\t\tawait this.providerOnDisconnect?.();\n\t}\n\n\tprivate skippedNonCloudReported = false;\n\t@debug()\n\tasync syncCloudConnection(state: 'connected' | 'disconnected', forceSync: boolean): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\t\t// Initially the condition on `this._session.cloud` has been added here: https://github.com/gitkraken/vscode-gitlens/commit/e95e70c430bd162924cc3bd5c1e8ab90e6293449#diff-4213141a45cccaab7aa2e40028b155a87eb913b07388485831403e60ce5555e4R237\n\t\t// I'm not sure about reasons, but it seems we want to replace it with the cloud session if it's connected.\n\t\t// Gradually we'll stop having non-cloud sessions.\n\t\t// However this is needed to be tested with PATs, e.g. with a GitLab PAT.\n\t\tif (this._session?.cloud === false && state !== 'connected') {\n\t\t\tif (this.id !== GitCloudHostIntegrationId.GitHub && !this.skippedNonCloudReported) {\n\t\t\t\tthis.ctx.hooks?.session?.onRefreshSkipped?.({\n\t\t\t\t\tid: this.id,\n\t\t\t\t\treason: 'skip-non-cloud',\n\t\t\t\t\tcloud: false,\n\t\t\t\t});\n\t\t\t\tthis.skippedNonCloudReported = true;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (state) {\n\t\t\tcase 'connected': {\n\t\t\t\tconst oldSession = this._session;\n\t\t\t\tlet resyncing = false;\n\t\t\t\tif (forceSync) {\n\t\t\t\t\t// Reset our stored session so that we get a new one from the cloud\n\t\t\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\t\t\tawait authProvider.deleteSession(this.authProviderDescriptor);\n\t\t\t\t\t// The stored token was just deleted. Not left to the token-changed check below, which needs\n\t\t\t\t\t// an `oldSession` — see `onStoredTokensReplaced`.\n\t\t\t\t\tthis.onStoredTokensReplaced();\n\t\t\t\t\t// Reset the session and clear our \"stay disconnected\" flag\n\t\t\t\t\tthis._session = undefined;\n\t\t\t\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\t\t\t\t\tresyncing = true;\n\t\t\t\t} else {\n\t\t\t\t\t// Only sync if we're not connected and not disabled and don't have pending errors\n\t\t\t\t\tif (\n\t\t\t\t\t\tthis._session != null ||\n\t\t\t\t\t\tthis.requestExceptionCount > 0 ||\n\t\t\t\t\t\tthis.ctx.storage.getWorkspace(this.connectedKey) === false\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tforceSync = true;\n\t\t\t\t}\n\n\t\t\t\t// sync option, rather than createIfNeeded, makes sure we don't call connectCloudIntegrations and open a gkdev window\n\t\t\t\t// if there was no session or some problem fetching/refreshing the existing session from the cloud api\n\t\t\t\tlet newSession: ProviderAuthenticationSession | undefined;\n\t\t\t\tlet refetchFailed = false;\n\t\t\t\ttry {\n\t\t\t\t\tnewSession = await this.ensureSession({ sync: forceSync });\n\t\t\t\t} catch (ex) {\n\t\t\t\t\t// Not evidence the connection is gone (#5569). Also leaves `_session` as-is rather than\n\t\t\t\t\t// latching null, so the next access can re-resolve it.\n\t\t\t\t\tif (!isCancellationError(ex)) {\n\t\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\t}\n\t\t\t\t\trefetchFailed = true;\n\t\t\t\t}\n\n\t\t\t\tif (oldSession && newSession && newSession.accessToken !== oldSession.accessToken) {\n\t\t\t\t\tthis.resetRequestExceptionCount('all');\n\t\t\t\t}\n\n\t\t\t\t// The forced re-sync above deleted the cloud secret but kept the descriptor to avoid UI churn.\n\t\t\t\t// Drop it only when the replacement fetch came back definitively empty, so a connection whose\n\t\t\t\t// token is really gone is cleanly disconnected (#5497) while a healthy one survives a blip (#5569).\n\t\t\t\tif (resyncing && newSession == null && !refetchFailed) {\n\t\t\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\t\t\tawait authProvider.deleteSession(this.authProviderDescriptor, { preserveConfigured: false });\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'disconnected':\n\t\t\t\tawait this.disconnect({ silent: true });\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Proves the credential with an uncached check, rejecting with the provider's `AuthenticationError` when it is\n\t * refused, and with anything else when the check proves nothing. A `403` proves nothing either: the credential\n\t * authenticated and was only denied the check's request.\n\t *\n\t * Implemented by the providers whose reads can reach their scopes without an uncached request to the connection\n\t * first: discovery served from a per-token cache (Azure DevOps, Bitbucket, Jira Cloud), or an SDK fan-out across\n\t * the requested repositories (Bitbucket Data Center). There, a dead token comes back as scoped refusals with no\n\t * failure of the connection: the same shape as one scope refusing a sound credential.\n\t * {@link confirmScopedAuthFailures} uses this to tell the two apart.\n\t */\n\tprotected validateCredential?(session: ProviderAuthenticationSession): Promise<void>;\n\n\t/**\n\t * Credentials that passed {@link validateCredential} within the last minute. A probe already in flight is\n\t * shared rather than repeated, and a refused one is not kept. Cleared whenever a credential is refused (see\n\t * {@link handleProviderException}), so a pass is never vouched for past a refusal this instance has seen.\n\t */\n\tprivate readonly _validatedCredentials = new PromiseCache<string, void>({ createTTL: 60 * 1000, capacity: 10 });\n\n\t/**\n\t * Whether a scope's refusal is the credential's own, by the provider's account of it: e.g. a token missing the\n\t * OAuth scopes the read needs. The probe cannot see that, because it proves the token authenticates, not that\n\t * it is authorized. So {@link confirmScopedAuthFailures} publishes one for the connection, unscoped, which asks\n\t * for the reconnect (consenting to the scopes again) that fixes it, and keeps the results of the scopes that\n\t * answered.\n\t */\n\tprotected isCredentialRefusal?(refusal: ProviderRefusal): boolean;\n\n\t/**\n\t * Names why a confirmed credential was refused by one scope, from what the refusal said (see\n\t * `ProviderWarning.cause`). Called only by {@link confirmScopedAuthFailures}, once the credential passed: the\n\t * refusals this names are indistinguishable from a dead credential until then.\n\t */\n\tprotected describeRefusal?(\n\t\tsession: ProviderAuthenticationSession,\n\t\trefusal: ProviderRefusal,\n\t\tscope: ProviderWarningScope,\n\t): ProviderWarningCause | undefined;\n\n\t/**\n\t * Settles what a read's only-scoped authentication failures mean, when the provider can tell (see\n\t * {@link validateCredential}).\n\t *\n\t * A refused credential is thrown: the caller's `catch` then fails the read as a whole, exactly as it does when\n\t * discovery is not cached and the first request is the one refused. Without this, a token revoked after\n\t * discovery would publish scoped `auth` warnings, which a consumer must not answer with a reconnect (see\n\t * `ProviderWarning.scope`), next to results served from the cache, for as long as the token stays stored.\n\t *\n\t * A confirmed credential makes each refusal the scope's own, so its cause is named through\n\t * {@link describeRefusal}. A probe that fails for another reason proves nothing either way and leaves the read\n\t * as it was.\n\t *\n\t * A refusal the provider pins on the credential itself (see {@link isCredentialRefusal}) is published for the\n\t * connection instead, without a probe.\n\t *\n\t * A token that passed within the last minute is not probed again, so a scope that keeps refusing it costs one\n\t * probe a minute, and a revocation right after a probe can take up to a minute to surface: until then its\n\t * refusals stay scoped, and may be named. A probe that proved nothing is not remembered, so the next read probes\n\t * again, and a credential no probe can confirm (e.g. a Bitbucket Data Center project access token) is probed on\n\t * every read that has scoped refusals.\n\t */\n\tprotected async confirmScopedAuthFailures(\n\t\tsession: ProviderAuthenticationSession,\n\t\tmetadata: CollectionMetadata | undefined,\n\t): Promise<void> {\n\t\tif (!hasOnlyScopedAuthFailures(metadata)) return;\n\n\t\t// Published for the connection, as a refusal outside any scope would be, while the scopes that answered keep\n\t\t// their results. That already asks for a reconnect, so there is nothing left for a probe to settle.\n\t\tif (this.markCredentialRefusals(metadata)) return;\n\t\tif ((await this.confirmCredential(session)) !== 'confirmed') return;\n\n\t\tthis.nameRefusalCauses(session, metadata);\n\t}\n\n\t/**\n\t * Settles what a batch read's authentication refusals mean before the call is judged, as\n\t * {@link confirmScopedAuthFailures} settles an account-wide read's: each refused slot comes back carrying the scope\n\t * failure it was recorded as, against the scope its target names (see {@link batchTargetScope}), and a read\n\t * publishes that instead of the bare reason.\n\t *\n\t * When any target answered, that proved the credential, so each refusal is its own target's. When every target\n\t * failed, the call fails as a whole, as it always has, unless every failure is a refusal of a credential that checks\n\t * out. Every target refusing is also what a dead credential looks like, so the credential is checked once first,\n\t * with {@link validateCredential}. A check that proves nothing leaves the refusals scoped and unnamed, as it leaves\n\t * an account-wide read's. A provider with no check cannot tell, and a refusal it pins on the credential (see\n\t * {@link isCredentialRefusal}) needs none, so either still fails the call.\n\t */\n\tprotected async settleBatchRefusals<T>(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly BatchTarget[],\n\t\tslots: PromiseSettledResult<T>[],\n\t): Promise<BatchSlot<T>[]> {\n\t\tconst failures = slots.map((slot, i) =>\n\t\t\tslot.status === 'rejected' && slot.reason instanceof AuthenticationError\n\t\t\t\t? toCollectionScopeFailure(batchTargetScope(this.id, targets[i]), slot.reason)\n\t\t\t\t: undefined,\n\t\t);\n\t\tconst refused = failures.filter(f => f != null);\n\t\tif (!refused.length) {\n\t\t\tthrowIfAllSettledFailed(slots);\n\t\t\treturn slots;\n\t\t}\n\n\t\tconst metadata: CollectionMetadata = { completeness: 'partial', failures: refused };\n\t\tconst credentialRefused = this.markCredentialRefusals(metadata);\n\t\tif (slots.some(slot => slot.status === 'fulfilled')) {\n\t\t\tthis.nameRefusalCauses(session, metadata);\n\t\t} else if (credentialRefused) {\n\t\t\tthrowIfAllSettledFailed(slots);\n\t\t} else if (refused.length < slots.length) {\n\t\t\t// Fails on a target that failed for another reason: failing on a refusal would run the credential's own\n\t\t\t// recovery (a session expiry or a disconnect strike) for what may be only its target's refusal.\n\t\t\tthrowIfAllSettledFailed(slots.filter((_, i) => failures[i] == null));\n\t\t} else {\n\t\t\tconst confirmation = await this.confirmCredential(session);\n\t\t\tif (confirmation == null) {\n\t\t\t\tthrowIfAllSettledFailed(slots);\n\t\t\t} else if (confirmation === 'confirmed') {\n\t\t\t\tthis.nameRefusalCauses(session, metadata);\n\t\t\t}\n\t\t}\n\n\t\treturn slots.map((slot, i) => {\n\t\t\tconst failure = failures[i];\n\t\t\treturn slot.status === 'rejected' && failure != null ? { ...slot, failure: failure } : slot;\n\t\t});\n\t}\n\n\t/**\n\t * Shows one notice, and spends at most one strike, for a batch read whose targets failed with a server error or a\n\t * timeout. Our own clients leave both to the batch (see `reportRequestFailure`) instead of spending them per\n\t * target. `strike` is true only when every target failed and the call's own failure spent none: a call that\n\t * answered some targets showed the host is up, and budgets strikes the same way as the rest of its failures.\n\t */\n\tprotected reportDeferredRequestFailures(\n\t\tslots: readonly PromiseSettledResult<unknown>[] | undefined,\n\t\tstrike: boolean,\n\t): void {\n\t\tlet notify: (() => void) | undefined;\n\t\tfor (const slot of slots ?? []) {\n\t\t\tif (slot.status !== 'rejected') continue;\n\n\t\t\tnotify = getDeferredRequestFailure(slot.reason);\n\t\t\tif (notify != null) break;\n\t\t}\n\n\t\tif (notify == null) return;\n\n\t\tif (strike) {\n\t\t\tthis.trackRequestException();\n\t\t}\n\t\tnotify();\n\t}\n\n\t/** Marks the scoped refusals the provider pins on the credential (see {@link isCredentialRefusal}). */\n\tprivate markCredentialRefusals(metadata: CollectionMetadata | undefined): boolean {\n\t\tconst isCredentialRefusal = this.isCredentialRefusal?.bind(this);\n\t\treturn isCredentialRefusal != null && markCredentialRefusals(metadata, isCredentialRefusal);\n\t}\n\n\t/**\n\t * Checks the credential with {@link validateCredential}, rethrowing the provider's refusal when it is refused.\n\t * `undefined` when the provider has no check, and `'unproven'` when the check proved nothing either way.\n\t */\n\tprivate async confirmCredential(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<'confirmed' | 'unproven' | undefined> {\n\t\tconst validateCredential = this.validateCredential?.bind(this);\n\t\tif (validateCredential == null) return undefined;\n\n\t\t// Keyed by the address as well as the token: a self-managed instance serves every installation on its host,\n\t\t// and one installation accepting a token says nothing about another.\n\t\tconst key = [session.domain, session.baseUrl ?? '', session.accessToken].join('\\n');\n\t\ttry {\n\t\t\tawait this._validatedCredentials.getOrCreate(key, () => validateCredential(session));\n\t\t} catch (ex) {\n\t\t\t// A 403 answered a credential that authenticated and was only denied the check's own request, so it proves\n\t\t\t// nothing about the scopes' refusals either.\n\t\t\tif (ex instanceof AuthenticationError && ex.reason !== AuthenticationErrorReason.Forbidden) throw ex;\n\n\t\t\treturn 'unproven';\n\t\t}\n\t\treturn 'confirmed';\n\t}\n\n\t/** Names each scoped refusal's cause (see {@link describeRefusal}); only once the credential is proven. */\n\tprivate nameRefusalCauses(session: ProviderAuthenticationSession, metadata: CollectionMetadata | undefined): void {\n\t\tconst describeRefusal = this.describeRefusal?.bind(this);\n\t\tif (describeRefusal == null) return;\n\n\t\tattributeScopedAuthFailures(metadata, (refusal, scope) => describeRefusal(session, refusal, scope));\n\t}\n\n\t/** Returns whether it spent a strike toward disconnecting (see {@link trackRequestException}). */\n\tprotected handleProviderException(\n\t\tsyncReqUsecase: SyncReqUsecase,\n\t\tex: Error,\n\t\toptions?: { scope?: ScopedLogger | undefined; silent?: boolean; connectionId?: string },\n\t): boolean {\n\t\tif (isCancellationError(ex)) return false;\n\n\t\t// A refused credential may be one a probe passed moments ago: stop vouching for it.\n\t\tif (ex instanceof AuthenticationError) {\n\t\t\tthis._validatedCredentials.clear();\n\t\t}\n\n\t\tif (options?.scope != null) {\n\t\t\toptions.scope.error(ex);\n\t\t} else {\n\t\t\tLogger.error(ex);\n\t\t}\n\n\t\t// A per-connection (multi-account) read resolved its session through `resolveReadSession`'s\n\t\t// `connectionId` branch, which deliberately never touches the cached primary `_session`. So the\n\t\t// primary-session recovery below cannot apply to it: expiring `_session` would mark a session this\n\t\t// read never used, while the rejected connection kept its stored token and re-sent it on every\n\t\t// later read — a token the provider has already refused (expired scopes, a revoked grant, an\n\t\t// uninstalled app) is not self-healing, so the read failed identically until the user reconnected\n\t\t// by hand. Record the rejection against the connection instead; `resolveReadSession` consumes it\n\t\t// and forces the cloud `/refresh` on the next read of that connection. When there is nothing to\n\t\t// recover, fall through to the shared failure budget, which disconnects after\n\t\t// `requestExceptionLimit` and surfaces the reconnect prompt.\n\t\tif (ex instanceof AuthenticationError && options?.connectionId) {\n\t\t\tif (this._rejectedTokens.recordRejection(options.connectionId)) return false;\n\n\t\t\tthis.trackRequestException(options);\n\t\t\treturn true;\n\t\t}\n\n\t\tif (ex instanceof AuthenticationError && this._session?.cloud && !this.hasSessionSyncRequests()) {\n\t\t\tthis.requestSessionSyncForUsecase(syncReqUsecase);\n\t\t\tthis._session = {\n\t\t\t\t...this._session,\n\t\t\t\texpiresAt: new Date(Date.now() - 1),\n\t\t\t};\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!(ex instanceof AuthenticationError) && !(ex instanceof RequestClientError)) return false;\n\n\t\tthis.trackRequestException(options);\n\t\treturn true;\n\t}\n\n\tprivate missingExpirityReported = false;\n\t@gate()\n\tprotected async refreshSessionIfExpired(scope?: ScopedLogger): Promise<void> {\n\t\tif (this._session?.expiresAt != null && this._session.expiresAt < new Date()) {\n\t\t\t// The current session is expired, so get the latest from the cloud and refresh if needed\n\t\t\ttry {\n\t\t\t\tawait this.syncCloudConnection('connected', true);\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex);\n\t\t\t}\n\t\t} else if (\n\t\t\tthis._session?.expiresAt == null &&\n\t\t\tthis.id !== GitCloudHostIntegrationId.GitHub &&\n\t\t\t!this.missingExpirityReported\n\t\t) {\n\t\t\tthis.ctx.hooks?.session?.onRefreshSkipped?.({\n\t\t\t\tid: this.id,\n\t\t\t\treason: 'missing-expiry',\n\t\t\t\tcloud: this._session?.cloud,\n\t\t\t});\n\t\t\tthis.missingExpirityReported = true;\n\t\t}\n\t}\n\n\t@trace()\n\ttrackRequestException(options?: { silent?: boolean }): void {\n\t\tthis.requestExceptionCount++;\n\n\t\tif (this.requestExceptionCount >= IntegrationBase.requestExceptionLimit && this._session !== null) {\n\t\t\tif (!options?.silent) {\n\t\t\t\tthis.ctx.hooks?.ui?.onDisconnectedAfterTooManyFailures?.(this.name);\n\t\t\t}\n\t\t\tvoid this.disconnect({ currentSessionOnly: true });\n\t\t}\n\t}\n\n\t@gate()\n\t@trace({ exit: true })\n\tasync isConnected(): Promise<boolean> {\n\t\treturn (await this.getSession('integrations')) != null;\n\t}\n\n\t@gate()\n\tprivate async ensureSession(\n\t\toptions:\n\t\t\t| {\n\t\t\t\t\tcreateIfNeeded?: boolean;\n\t\t\t\t\tforceNewSession?: boolean;\n\t\t\t\t\tsync?: never;\n\t\t\t\t\tsource?: Sources;\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tcreateIfNeeded?: never;\n\t\t\t\t\tforceNewSession?: never;\n\t\t\t\t\tsync: boolean;\n\t\t\t\t\tsource?: Sources;\n\t\t\t },\n\t): Promise<ProviderAuthenticationSession | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst { createIfNeeded, forceNewSession, source, sync } = options;\n\t\tif (this._session != null) {\n\t\t\tif (this.isSessionForIntegrationHost(this._session)) return this._session;\n\n\t\t\tthis._session = null;\n\t\t}\n\t\tif (this.ctx.config.isIntegrationsEnabled?.() === false) return undefined;\n\n\t\tif (createIfNeeded || sync) {\n\t\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\t\t} else if (this.ctx.storage.getWorkspace(this.connectedKey) === false) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet session: ProviderAuthenticationSession | undefined | null;\n\t\ttry {\n\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\tsession = await authProvider.getSession(\n\t\t\t\tthis.authProviderDescriptor,\n\t\t\t\tsync\n\t\t\t\t\t? { sync: sync, source: source }\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tcreateIfNeeded: createIfNeeded,\n\t\t\t\t\t\t\tforceNewSession: forceNewSession,\n\t\t\t\t\t\t\tsource: source,\n\t\t\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (session?.expiresAt != null && session.expiresAt < new Date()) {\n\t\t\t\tsession = null;\n\t\t\t}\n\t\t\tif (session != null && !this.isSessionForIntegrationHost(session)) {\n\t\t\t\tsession = undefined;\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\n\t\t\t// Only interactive paths can prompt for consent, so a sync failure must reach the rethrow below.\n\t\t\tif (!sync && ex instanceof Error && ex.message.includes('User did not consent')) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// On a forced re-sync, propagate so syncCloudConnection can tell a failure from a definitive empty\n\t\t\t// result (#5569); other callers keep swallowing to null so reads never throw.\n\t\t\tif (sync) {\n\t\t\t\t// Throwing skips the reset below, and a lingering count blocks the next non-forced sync.\n\t\t\t\tthis.smoothifyRequestExceptionCount();\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\tsession = null;\n\t\t}\n\n\t\tif (session === undefined && !createIfNeeded && !sync) {\n\t\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\t\t}\n\n\t\tthis._session = session ?? null;\n\t\tthis.smoothifyRequestExceptionCount();\n\n\t\tif (session != null) {\n\t\t\tawait this.ctx.storage.storeWorkspace(this.connectedKey, true);\n\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tthis._onDidChange.fire();\n\t\t\t\tthis.didChangeConnection?.fire({ integration: this, key: this.key, reason: 'connected' });\n\t\t\t\t// Fired detached, so there is no caller left to catch anything: every implementor is async, and\n\t\t\t\t// a rejection would surface as a process-level unhandled rejection in the host. It is a\n\t\t\t\t// best-effort warm-up, so swallow the failure with a warning instead.\n\t\t\t\tvoid (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.providerOnConnect?.();\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tscope?.warn(\n\t\t\t\t\t\t\t`Failed to run providerOnConnect for ${this.key}: ${ex instanceof Error ? ex.message : String(ex)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t}\n\n\t\treturn session ?? undefined;\n\t}\n\n\tgetIgnoreSSLErrors(): boolean | 'force' {\n\t\treturn this.authenticationService.ignoreSSLErrors(this);\n\t}\n\n\tasync searchMyIssues(\n\t\tresource?: ResourceDescriptor,\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined>;\n\tasync searchMyIssues(\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined>;\n\t@trace()\n\tasync searchMyIssues(\n\t\tresources?: ResourceDescriptor | ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchMyIssuesResult(resources, cancellation, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link searchMyIssues}. Recovers thrown errors into `{ error }` so callers\n\t * (e.g. the ProviderBackend account-wide issues read) can surface a per-provider warning instead of a\n\t * silent empty result. Returns the normalized {@link IssueShape} (there is no raw account-wide issue read).\n\t */\n\tasync searchMyIssuesResult(\n\t\tresources?: ResourceDescriptor | ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<IssueShape[] | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tconst issues = await this.searchProviderMyIssues(\n\t\t\t\tsession,\n\t\t\t\tresources != null ? (Array.isArray(resources) ? resources : [resources]) : undefined,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tthis.resetRequestExceptionCount('searchMyIssues');\n\t\t\treturn { value: issues, duration: performance.now() - start };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('searchMyIssues', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex), duration: performance.now() - start };\n\t\t}\n\t}\n\n\tprotected abstract searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined>;\n\n\t/**\n\t * Paging/truncation-aware variant of {@link searchProviderMyIssues}. The default wraps the normalized read\n\t * as a complete single page; providers with native cursors or fan-out metadata override it.\n\t */\n\tprotected async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\t_options?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\t// The default read has no assignee scoping to broaden, so `_options` is inert here; a provider whose\n\t\t// account-wide read is user-scoped (GitHub/GitLab/Azure) overrides this and honors `includeAllAssignees`.\n\t\tconst values = await this.searchProviderMyIssues(session, resources, cancellation);\n\t\tif (values == null) return undefined;\n\t\treturn { values: values, truncated: false };\n\t}\n\n\t/**\n\t * Result-returning, truncation-aware account-wide issue read. Recovers thrown errors into `{ error }` and\n\t * carries the `truncated` flag so the ProviderBackend facade can report an incomplete read honestly.\n\t */\n\tasync searchMyIssuesWithTruncationResult(\n\t\tresources?: ResourceDescriptor | ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<IntegrationResult<AccountWideIssuesResult | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst result = await this.searchProviderMyIssuesWithTruncation(\n\t\t\t\tsession,\n\t\t\t\tresources != null ? (Array.isArray(resources) ? resources : [resources]) : undefined,\n\t\t\t\tcancellation,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tawait this.confirmScopedAuthFailures(session, result?.metadata);\n\t\t\tthis.resetRequestExceptionCount('searchMyIssues');\n\t\t\treturn { value: result };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('searchMyIssues', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t@trace()\n\tasync getLinkedIssueOrPullRequest(\n\t\tresource: T,\n\t\tlink: { id: string; key: string },\n\t\toptions?: { expiryOverride?: boolean | number; type?: IssueOrPullRequestType },\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected) return undefined;\n\n\t\tawait this.refreshSessionIfExpired(scope);\n\n\t\tconst issueOrPR = this.ctx.cache.getIssueOrPullRequest(\n\t\t\tlink.key,\n\t\t\toptions?.type,\n\t\t\tresource,\n\t\t\tthis,\n\t\t\t() => ({\n\t\t\t\tvalue: (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await this.getProviderLinkedIssueOrPullRequest(\n\t\t\t\t\t\t\tthis._session!,\n\t\t\t\t\t\t\tresource,\n\t\t\t\t\t\t\tlink,\n\t\t\t\t\t\t\toptions?.type,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.resetRequestExceptionCount('getIssueOrPullRequest');\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tthis.handleProviderException('getIssueOrPullRequest', ex, { scope: scope });\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t})(),\n\t\t\t}),\n\t\t\toptions,\n\t\t);\n\t\treturn issueOrPR;\n\t}\n\n\tprotected abstract getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t\tlink: { id: string; key: string },\n\t\ttype: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined>;\n\n\tasync getIssue(\n\t\tresource: T,\n\t\tid: string,\n\t\toptions?: { connectionId?: string; expiryOverride?: boolean | number },\n\t): Promise<Issue | undefined> {\n\t\treturn (await this.getIssueResult(resource, id, options))?.value;\n\t}\n\n\tgetIssueResult(\n\t\tresource: T,\n\t\tid: string,\n\t\toptions?: { connectionId?: string; expiryOverride?: boolean | number },\n\t): Promise<IntegrationResult<Issue | undefined>> {\n\t\treturn this.getIssueResultCore(resource, id, options, session => this.getProviderIssue(session, resource, id));\n\t}\n\n\t@trace()\n\tprotected async getIssueResultCore(\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t\toptions: { connectionId?: string; expiryOverride?: boolean | number } | undefined,\n\t\tgetProviderIssue: (session: ProviderAuthenticationSession) => Promise<Issue | undefined>,\n\t): Promise<IntegrationResult<Issue | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { connectionId: requestedConnectionId, expiryOverride } = options ?? {};\n\t\tconst connectionId = requestedConnectionId || undefined;\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst issue = await getCachedIssue({\n\t\t\t\tcache: this.ctx.cache,\n\t\t\t\tid: id,\n\t\t\t\tresource: resource,\n\t\t\t\tintegration: this,\n\t\t\t\tload: async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst issue = await getProviderIssue(session);\n\t\t\t\t\t\tthis.resetRequestExceptionCount('getIssue');\n\t\t\t\t\t\treturn issue;\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tif (!isCancellationError(ex)) {\n\t\t\t\t\t\t\tthis.handleProviderException('getIssue', toError(ex), {\n\t\t\t\t\t\t\t\tscope: scope,\n\t\t\t\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tcacheOptions: {\n\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t\texpiryOverride: expiryOverride,\n\t\t\t\t\tetag: `${this.id}:${this.maybeConnected ?? false}:${this.getSessionFingerprint(session)}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn { value: issue };\n\t\t} catch (ex) {\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t\tid: string,\n\t): Promise<Issue | undefined>;\n\n\tasync getCurrentAccount(options?: {\n\t\tavatarSize?: number;\n\t\tconnectionId?: string;\n\t\texpiryOverride?: boolean | number;\n\t}): Promise<Account | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { connectionId: requestedConnectionId, expiryOverride, ...opts } = options ?? {};\n\t\tconst connectionId = requestedConnectionId || undefined;\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\tconst sessionFingerprint = this.getSessionFingerprint(session);\n\n\t\tconst currentAccount = await this.ctx.cache.getCurrentAccount(\n\t\t\tthis,\n\n\t\t\t(cacheable: any) => ({\n\t\t\t\tvalue: (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst account = await this.getProviderCurrentAccount?.(session, opts);\n\t\t\t\t\t\tthis.resetRequestExceptionCount('getCurrentAccount');\n\t\t\t\t\t\treturn account;\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tif (isCancellationError(ex)) {\n\t\t\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.handleProviderException('getCurrentAccount', ex, {\n\t\t\t\t\t\t\tscope: scope,\n\t\t\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Invalidate the cache on error, except for auth errors\n\t\t\t\t\t\tif (!(ex instanceof AuthenticationError)) {\n\t\t\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Re-throw to the caller\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t})(),\n\t\t\t}),\n\t\t\t{\n\t\t\t\tconnectionId: connectionId,\n\t\t\t\texpiryOverride: expiryOverride,\n\t\t\t\texpireOnError: false,\n\t\t\t\tetag: `${this.id}:${this.maybeConnected ?? false}:${sessionFingerprint}`,\n\t\t\t},\n\t\t);\n\t\treturn currentAccount;\n\t}\n\n\tprotected getProviderCurrentAccount?(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: { avatarSize?: number },\n\t): Promise<Account | undefined>;\n\n\t/** Whether this integration can answer {@link getCurrentAccount} at all — Jira, Linear and Trello cannot. */\n\tget supportsCurrentAccount(): boolean {\n\t\treturn this.getProviderCurrentAccount != null;\n\t}\n\n\t/**\n\t * Resolves the account for a specific session/token — including connections other than the current\n\t * primary (multi-account) — using this integration's provider API base URL and auth type. Returns\n\t * undefined when the provider doesn't support account lookup. Uncached (callers cache per connection).\n\t */\n\tgetProviderAccountForSession(session: ProviderAuthenticationSession): Promise<Account | undefined> {\n\t\treturn this.getProviderCurrentAccount?.(session) ?? Promise.resolve(undefined);\n\t}\n\n\t@trace()\n\tasync getPullRequest(\n\t\tresource: T,\n\t\tid: string,\n\t\toptions?: { expiryOverride?: boolean | number; throwOnError?: boolean },\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected) return undefined;\n\n\t\tawait this.refreshSessionIfExpired(scope);\n\n\t\tconst pr = await this.ctx.cache.getPullRequest(\n\t\t\tid,\n\t\t\tresource,\n\t\t\tthis,\n\t\t\tcacheable => ({\n\t\t\t\tvalue: (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await this.getProviderPullRequest?.(this._session!, resource, id);\n\t\t\t\t\t\tthis.resetRequestExceptionCount('getPullRequest');\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t// A failed lookup is not an answer — and the by-id bucket never expires a miss.\n\t\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t\t\tthis.handleProviderException('getPullRequest', ex, { scope: scope });\n\t\t\t\t\t\tif (options?.throwOnError) throw ex;\n\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t})(),\n\t\t\t}),\n\t\t\t{ expiryOverride: options?.expiryOverride },\n\t\t);\n\t\treturn pr;\n\t}\n\n\tprotected getProviderPullRequest?(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t\tid: string,\n\t): Promise<PullRequest | undefined>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","names":[],"sources":["../../../../../plus/integrations/src/models/integration.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScope } from '@gitkraken/provider-apis';\nimport { getDeferredRequestFailure } from '@gitlens/git/errors.js';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '@gitlens/git/models/autolink.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type {\n\tIssueOrPullRequest,\n\tIssueOrPullRequestState,\n\tIssueOrPullRequestType,\n} from '@gitlens/git/models/issueOrPullRequest.js';\nimport type {\n\tPullRequest,\n\tPullRequestMergeableState,\n\tPullRequestReviewDecision,\n\tPullRequestReviewState,\n\tPullRequestState,\n\tPullRequestStatusCheckRollupState,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug, trace } from '@gitlens/utils/decorators/log.js';\nimport type { Disposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { fnv1aHash64 } from '@gitlens/utils/hash.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type {\n\tIntegrationAuthenticationProviderDescriptor,\n\tIntegrationAuthenticationSessionDescriptor,\n} from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { RejectedTokenTracker } from '../authentication/rejectedTokenTracker.js';\nimport type { ProviderRefusal, ProviderScopeFailure } from '../collectionMetadata.js';\nimport {\n\tattributeScopedAuthFailures,\n\thasOnlyScopedAuthFailures,\n\tmarkCredentialRefusals,\n\ttoCollectionScopeFailure,\n} from '../collectionMetadata.js';\nimport type { IntegrationIds, IssuesCloudHostIntegrationId, IssuesHostIntegrationIds } from '../constants.js';\nimport { GitCloudHostIntegrationId } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport { AuthenticationError, AuthenticationErrorReason, RequestClientError, toError } from '../errors.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport { providersMetadata } from '../providers/models.js';\nimport { isAzureProviderId } from '../providers/providerErrors.js';\nimport type { ProvidersApi } from '../providers/providersApi.js';\nimport { throwIfAllSettledFailed } from '../providers/utils/providerPaging.js';\nimport type { ProviderWarningCause, ProviderWarningScope } from '../results.js';\nimport type { Sources } from '../telemetry.js';\nimport { areDomainsOnSameHost } from '../utils/domain.utils.js';\nimport { isSelfManagedHostIntegrationId } from '../utils/integration.utils.js';\nimport type { GitHostIntegration } from './gitHostIntegration.js';\nimport { getCachedIssue } from './issueCache.js';\nimport type { AccountWideIssuesResult, SearchMyIssuesOptions } from './issueReads.js';\nimport type { IssuesIntegration } from './issuesIntegration.js';\n\nexport type Integration = GitHostIntegration | IssuesIntegration;\nexport type IntegrationById<T extends IntegrationIds> = T extends IssuesHostIntegrationIds\n\t? IssuesIntegration\n\t: GitHostIntegration;\nexport type IntegrationType = 'git' | 'issues';\n\n// The issue-read contracts live in their own module (pure data, and their relationship to each other is the\n// point of reading them together); re-exported here so the providers that implement these reads keep one import.\nexport type {\n\tAccountWideIssuesResult,\n\tProviderIssueSearchPage,\n\tProviderSearchCount,\n\tSearchMyIssuesOptions,\n} from './issueReads.js';\nexport type { ProviderPullRequestCount, ProviderPullRequestSearchPage } from './pullRequestReads.js';\n\n// Keep this in step with `isSelfManagedHostIntegrationId`: it is the type-level twin of that predicate, and a\n// disagreement compiles cleanly while the runtime writes a domain-keyed key the type says is unkeyed (so the\n// local-disconnect flag is written under one key and read under another).\nexport type IntegrationKey<T extends IntegrationIds = IntegrationIds> = T extends\n\t| GitCloudHostIntegrationId\n\t| IssuesCloudHostIntegrationId\n\t? `${T}`\n\t: `${T}:${string}`;\n\nexport type IntegrationConnectedKey<T extends IntegrationIds = IntegrationIds> = `connected:${IntegrationKey<T>}`;\n\nexport type IntegrationResult<T> =\n\t| { value: T; duration?: number; error?: Error }\n\t| { error: Error; duration?: number; value?: never }\n\t| undefined;\n\n/**\n * One target's answer in a batch read. A slot rejected by an authentication refusal carries the scope failure it was\n * recorded as (see `IntegrationBase.settleBatchRefusals`).\n */\nexport type BatchSlot<T> = PromiseFulfilledResult<T> | (PromiseRejectedResult & { failure?: ProviderScopeFailure });\n\n/**\n * An input a pull request's etag can be widened to, each of which a host changes without moving `updatedAt`. Every\n * entry costs its own fields in the cheap check, so a caller picks only the ones it needs. `'checks'` is the check\n * rollup.\n */\nexport type PullRequestEtagInclude = 'mergeable' | 'reviewDecision' | 'checks';\n\n/** Every {@link PullRequestEtagInclude}, in the canonical order an etag lists them in. */\nexport const pullRequestEtagIncludes: readonly PullRequestEtagInclude[] = ['mergeable', 'reviewDecision', 'checks'];\n\n/**\n * The change state a batch read's etag is computed from (see `reads/etag.ts`), in the NORMALIZED vocabulary of\n * `PullRequestShape` — the values a full row carries after its provider's whole conversion chain, so a cheap\n * check and a full read of the same pull request yield the same fields. Each of `mergeableState`, `reviewDecision`\n * and `statusCheckRollupState` counts only when its {@link PullRequestEtagInclude} is requested.\n */\nexport interface PullRequestEtagFields {\n\tstate: PullRequestState;\n\tisDraft?: boolean;\n\tupdatedDate: Date;\n\theadSha?: string;\n\tmergeableState?: PullRequestMergeableState;\n\treviewDecision?: PullRequestReviewDecision;\n\tstatusCheckRollupState?: PullRequestStatusCheckRollupState;\n\t/**\n\t * A fingerprint of the fields a host changes without moving an update time ({@link pullRequestRevision}); set\n\t * only for hosts that keep none (Azure DevOps and Server). Part of the etag whenever it is set, not an include.\n\t */\n\trevision?: string;\n}\n\n/**\n * The fields of a pull request {@link pullRequestRevision} reads, as a full row carries them. A `PullRequestShape`\n * is one, so a full read passes its row as is and a cheap check builds one from the same mapped values.\n */\nexport interface PullRequestRevisionSource {\n\treadonly title: string;\n\treadonly body?: string;\n\treadonly refs?: { readonly base: { readonly branch: string } };\n\treadonly reviewRequests?: readonly PullRequestRevisionReviewer[];\n\treadonly latestReviews?: readonly PullRequestRevisionReviewer[];\n}\n\ninterface PullRequestRevisionReviewer {\n\treadonly reviewer: { readonly id: string };\n\treadonly state: PullRequestReviewState;\n}\n\n/**\n * {@link PullRequestEtagFields.revision}: a 64-bit hash of a pull request's title, description, target branch and\n * reviewers (each one's id and review state, in a canonical order). Hashed to keep the etag short, so a collision is\n * the only way a change can hide, at about 2^-64.\n */\nexport function pullRequestRevision(pr: PullRequestRevisionSource): string {\n\treturn fnv1aHash64(\n\t\tJSON.stringify([\n\t\t\tpr.title,\n\t\t\tpr.body ?? null,\n\t\t\tpr.refs?.base.branch ?? null,\n\t\t\ttoRevisionReviewers(pr.reviewRequests),\n\t\t\ttoRevisionReviewers(pr.latestReviews),\n\t\t]),\n\t);\n}\n\n/** Each reviewer as its serialized `[id, state]`, sorted, so the host's order never moves the revision. */\nfunction toRevisionReviewers(reviewers: readonly PullRequestRevisionReviewer[] | undefined): string[] | null {\n\treturn reviewers?.map(r => JSON.stringify([r.reviewer.id, r.state])).sort() ?? null;\n}\n\n/**\n * An input an issue's etag can be widened to, which a host changes without moving `updatedAt`. `'reactions'` is the\n * thumbs-up count, and widens the etag only on hosts whose rows really fetch one (GitHub/GHE and GitLab).\n */\nexport type IssueEtagInclude = 'reactions';\n\n/** Every {@link IssueEtagInclude}, in the canonical order an etag lists them in. */\nexport const issueEtagIncludes: readonly IssueEtagInclude[] = ['reactions'];\n\n/** The issue twin of {@link PullRequestEtagFields}, in {@link IssueShape}'s normalized vocabulary. */\nexport interface IssueEtagFields {\n\tstate: IssueOrPullRequestState;\n\tupdatedDate: Date;\n\t/** Counts only when `'reactions'` is requested, and only from a row whose read fetched reactions. */\n\tthumbsUpCount?: number;\n}\n\n/** A batch read's target, in the form its provider addresses it by: a repository coordinate, or a tracker's resource. */\ntype BatchTarget = { owner: string; repo: string; project?: string } | { resourceId: string };\n\n/**\n * The scope a batch read records a refused target under. It is decided here rather than in the read, which has the\n * same targets, because naming the refusal's cause (`describeRefusal`) needs it too. Azure DevOps: the organization\n * and project, since its refusals come from those (an organization's OAuth policy or tenant, a project's permissions)\n * and its work items belong to no repository. Every other git host: the repository, `owner/repo`, which is how its\n * repo-scoped reads record one. A tracker: the resource.\n */\nfunction batchTargetScope(providerId: IntegrationIds, target: BatchTarget): CollectionScope {\n\tif ('resourceId' in target) return { providerId: providerId, resourceId: target.resourceId };\n\tif (isAzureProviderId(providerId)) {\n\t\treturn { providerId: providerId, resourceId: target.owner, projectId: target.project };\n\t}\n\treturn { providerId: providerId, repositoryId: `${target.owner}/${target.repo}` };\n}\n\ntype SyncReqUsecase = Exclude<\n\t| 'getAccountForCommit'\n\t| 'getAccountForEmail'\n\t| 'getAccountForResource'\n\t| 'getCurrentAccount'\n\t| 'getDefaultBranch'\n\t| 'getIssue'\n\t| 'getIssueOrPullRequest'\n\t| 'getIssuesBatch'\n\t| 'getIssuesEtagFields'\n\t| 'getIssuesForProject'\n\t| 'getIssuesForRepos'\n\t| 'getMyPullRequestsForUser'\n\t| 'getOrganizationsForUser'\n\t| 'getProjectsForOrg'\n\t| 'getProjectsForResources'\n\t| 'getPullRequest'\n\t| 'getPullRequestsBatch'\n\t| 'getPullRequestsEtagFields'\n\t| 'getPullRequestsForBranches'\n\t| 'getRepositoriesForOrg'\n\t| 'getRepositoriesForUser'\n\t| 'getPullRequestForBranch'\n\t| 'getPullRequestForCommit'\n\t| 'getPullRequestsForRepos'\n\t| 'getRepositoryMetadata'\n\t| 'getResourcesForUser'\n\t| 'getSshSigningKeysForEmails'\n\t| 'countIssues'\n\t| 'countPullRequests'\n\t| 'mergePullRequest'\n\t| 'searchIssuesPage'\n\t| 'searchPullRequestsPage'\n\t| 'searchMyIssues'\n\t| 'searchMyPullRequests'\n\t| 'searchPullRequests',\n\t// excluding to show explicitly that we don't want to add 'all' key occasionally\n\t'all'\n>;\n\nexport abstract class IntegrationBase<\n\tID extends IntegrationIds = IntegrationIds,\n\tT extends ResourceDescriptor = ResourceDescriptor,\n> implements Disposable {\n\tabstract readonly type: IntegrationType;\n\n\tprivate readonly _onDidChange = new Emitter<void>();\n\tget onDidChange(): Event<void> {\n\t\treturn this._onDidChange.event;\n\t}\n\n\tconstructor(\n\t\tprotected readonly ctx: IntegrationServiceContext,\n\t\tprotected readonly authenticationService: IntegrationAuthenticationService,\n\t\tprotected readonly getProvidersApi: () => Promise<ProvidersApi>,\n\t\tprivate readonly didChangeConnection: Emitter<IntegrationConnectionChangeEvent>,\n\t) {}\n\n\tdispose(): void {\n\t\tthis._onDidChange.dispose();\n\t}\n\n\tabstract get authProvider(): IntegrationAuthenticationProviderDescriptor;\n\tabstract get id(): ID;\n\tprotected abstract get key(): IntegrationKey<ID>;\n\tabstract get name(): string;\n\tabstract get domain(): string;\n\n\tget authProviderDescriptor(): IntegrationAuthenticationSessionDescriptor {\n\t\treturn { domain: this.domain, scopes: this.authProvider.scopes };\n\t}\n\n\t/**\n\t * The `gl-provider-<key>` glicon key for this provider, which is NOT always its id.\n\t *\n\t * `providersMetadata.iconKey` is the field that says which glyph a provider draws with, and a\n\t * self-managed variant reuses its cloud family's — Jira Data Center draws with Jira's, because it is the\n\t * same product and the id only selects an icon. Returning the id instead named a glyph the registry has\n\t * no entry for, so a consumer rendering a `gl-provider-` glicon from `issue.provider.icon` requested a\n\t * glyph that does not exist.\n\t */\n\tget icon(): string {\n\t\treturn providersMetadata[this.id]?.iconKey ?? this.id;\n\t}\n\n\taccess(): Promise<boolean> {\n\t\treturn this.ctx.account.isTrialOrPaid();\n\t}\n\n\tautolinks():\n\t\t| (AutolinkReference | DynamicAutolinkReference)[]\n\t\t| Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\treturn [];\n\t}\n\n\tprivate get connectedKey(): IntegrationConnectedKey<ID> {\n\t\treturn `connected:${this.key}`;\n\t}\n\n\tget maybeConnected(): boolean | undefined {\n\t\treturn this._session === undefined ? undefined : this._session !== null;\n\t}\n\n\t/** Hash of the current session's access token. Changes on any token change (account switch or refresh). */\n\tprivate _sessionFingerprint: { session: ProviderAuthenticationSession; hash: string } | undefined;\n\tget sessionFingerprint(): string | undefined {\n\t\tif (this._session == null) return undefined;\n\n\t\tif (this._sessionFingerprint?.session !== this._session) {\n\t\t\tthis._sessionFingerprint = { session: this._session, hash: this.getSessionFingerprint(this._session) };\n\t\t}\n\t\treturn this._sessionFingerprint.hash;\n\t}\n\n\tprotected getSessionFingerprint(session: ProviderAuthenticationSession): string {\n\t\treturn fnv1aHash64(session.accessToken);\n\t}\n\n\tget connectionExpired(): boolean | undefined {\n\t\tif (this._session?.expiresAt == null) return undefined;\n\t\treturn new Date(this._session.expiresAt) < new Date();\n\t}\n\n\tprotected _session: ProviderAuthenticationSession | null | undefined;\n\tgetSession(\n\t\tsource: Sources,\n\t): ProviderAuthenticationSession | Promise<ProviderAuthenticationSession | undefined> | undefined {\n\t\tif (this._session === undefined) {\n\t\t\treturn this.ensureSession({ createIfNeeded: false, source: source });\n\t\t}\n\t\treturn this._session != null && this.isSessionForIntegrationHost(this._session) ? this._session : undefined;\n\t}\n\n\tprivate isSessionForIntegrationHost(session: ProviderAuthenticationSession): boolean {\n\t\tif (!isSelfManagedHostIntegrationId(this.id)) return true;\n\n\t\treturn areDomainsOnSameHost(this.domain, session.domain);\n\t}\n\n\t/**\n\t * Resolves the session to read as, for a per-connection (multi-account) read. When `connectionId` is\n\t * omitted this is the integration's primary session, resolved exactly like the existing read flow\n\t * (ensure-connected + refresh-if-expired). When set, it resolves THAT connection's session directly\n\t * from the auth provider — refreshing it if expired — WITHOUT disturbing the cached primary\n\t * `_session`. Returns undefined when the requested session can't be resolved (e.g. the connection is\n\t * gone or the provider isn't connected), so callers degrade to \"no results\".\n\t */\n\tprotected async resolveReadSession(\n\t\tconnectionId: string | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tsource?: Sources,\n\t): Promise<ProviderAuthenticationSession | undefined> {\n\t\tif (\n\t\t\tthis.ctx.config.isIntegrationsEnabled?.() === false ||\n\t\t\tthis.ctx.storage.getWorkspace(this.connectedKey) === false\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// A truthy connectionId targets a specific account; an empty string is not a real target, so it falls\n\t\t// through to the primary path below.\n\t\tif (connectionId) {\n\t\t\t// A read of this connection previously failed with an AuthenticationError, so the token in\n\t\t\t// storage is known-refused. Ask for a refresh through the GK cloud before reading again, which\n\t\t\t// is this branch's equivalent of the primary path's `refreshSessionIfExpired`. Claimed here so\n\t\t\t// the forced refresh runs at most once per rejection.\n\t\t\tconst refreshRejectedToken = this._rejectedTokens.claimRefresh(connectionId);\n\t\t\t// Degrade to \"no results\" on failure, matching the primary path (whose ensureSession/\n\t\t\t// refreshSessionIfExpired swallow errors) so read methods keep their never-throws contract.\n\t\t\ttry {\n\t\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\t\tconst session = await authProvider.getSession(\n\t\t\t\t\t{ ...this.authProviderDescriptor, connectionId: connectionId, cloud: true },\n\t\t\t\t\t{ source: source, refreshRejectedToken: refreshRejectedToken },\n\t\t\t\t);\n\t\t\t\treturn session != null && this.isSessionForIntegrationHost(session) ? session : undefined;\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected) return undefined;\n\n\t\tawait this.refreshSessionIfExpired(scope);\n\t\treturn this._session != null && this.isSessionForIntegrationHost(this._session) ? this._session : undefined;\n\t}\n\n\t@debug()\n\tasync connect(source: Sources): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Boolean(await this.ensureSession({ createIfNeeded: true, source: source }));\n\t\t} catch (_ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected providerOnConnect?(): void | Promise<void>;\n\n\t@gate()\n\t@debug()\n\tasync disconnect(options?: { silent?: boolean; currentSessionOnly?: boolean }): Promise<void> {\n\t\tif (options?.currentSessionOnly && this._session === null) return;\n\n\t\tconst connected = this._session != null;\n\n\t\tlet signOut = !options?.currentSessionOnly;\n\n\t\tif (connected && !options?.currentSessionOnly && !options?.silent) {\n\t\t\tconst decision = await this.ctx.hooks?.onConfirmDisconnect?.({\n\t\t\t\tintegrationName: this.name,\n\t\t\t\tofferSignOut: this.authenticationService.supports(this.authProvider.id),\n\t\t\t});\n\t\t\tif (decision == null) return;\n\n\t\t\tsignOut = decision.signOut;\n\t\t}\n\n\t\tif (signOut) {\n\t\t\t// Disconnecting a provider signs out of ALL its connected accounts (multi-account), not just the\n\t\t\t// primary — otherwise secondary connections' secrets/config would be orphaned. Removing a single\n\t\t\t// account is done via IntegrationService.deleteConnection instead. Pass this instance's descriptor\n\t\t\t// so self-managed disconnects stay scoped to this host: those group every host under one provider\n\t\t\t// id, so an unscoped clear would sign the user out of unrelated hosts. deleteAllSessions derives an\n\t\t\t// undefined domain for cloud providers, so they still clear every account as intended.\n\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\t// Awaited, not fire-and-forget: a caller that awaits `disconnect()` has to be able to rely on the\n\t\t\t// secrets and descriptors actually being gone when it resumes. Left floating, the only thing that\n\t\t\t// ever made this land in time was incidental scheduling slack — `syncCloudIntegrations` used to\n\t\t\t// await each provider in turn, so a later iteration's suspension let the previous provider's delete\n\t\t\t// finish. Syncing providers concurrently removes that slack and the clear was observably still\n\t\t\t// pending when the sync returned.\n\t\t\tawait authProvider.deleteAllSessions(this.authProviderDescriptor);\n\t\t}\n\n\t\tthis.resetRequestExceptionCount('all');\n\t\tthis._session = null;\n\n\t\tif (connected) {\n\t\t\t// Don't store the disconnected flag if silently disconnecting or disconnecting this only for\n\t\t\t// this current VS Code session (will be re-connected on next restart)\n\t\t\tif (!options?.currentSessionOnly && !options?.silent) {\n\t\t\t\tvoid this.ctx.storage.storeWorkspace(this.connectedKey, false).catch();\n\t\t\t}\n\n\t\t\tthis._onDidChange.fire();\n\t\t\tif (!options?.currentSessionOnly) {\n\t\t\t\tthis.didChangeConnection?.fire({ integration: this, key: this.key, reason: 'disconnected' });\n\t\t\t}\n\t\t}\n\n\t\tawait this.providerOnDisconnect?.();\n\t}\n\n\tprotected providerOnDisconnect?(): void | Promise<void>;\n\n\t@debug()\n\tasync reauthenticate(): Promise<void> {\n\t\t// `forceNewSession` below deletes the stored secrets to reconnect. Ahead of the guard — see\n\t\t// `onStoredTokensReplaced`.\n\t\tthis.onStoredTokensReplaced();\n\n\t\tif (this._session === undefined) return;\n\n\t\tthis._session = undefined;\n\t\tvoid (await this.ensureSession({ createIfNeeded: true, forceNewSession: true }));\n\t}\n\n\trefresh(): void {\n\t\tvoid this.ensureSession({ createIfNeeded: false });\n\t}\n\n\tprivate _syncRequestsPerFailedUsecase = new Set<SyncReqUsecase>();\n\thasSessionSyncRequests(): boolean {\n\t\treturn this._syncRequestsPerFailedUsecase.size > 0;\n\t}\n\trequestSessionSyncForUsecase(syncReqUsecase: SyncReqUsecase): void {\n\t\tthis._syncRequestsPerFailedUsecase.add(syncReqUsecase);\n\t}\n\n\t/**\n\t * The per-connection counterpart of the primary session's expire-and-resync recovery: a token the\n\t * provider refused is refreshed once through the GK cloud, which exchanges the refresh token server-side\n\t * (the client never holds one). Armed by {@link handleProviderException} and consumed by\n\t * {@link resolveReadSession}; a rejection it declines belongs to {@link trackRequestException} instead.\n\t */\n\tprivate readonly _rejectedTokens = new RejectedTokenTracker();\n\n\t/**\n\t * Called by every path that replaces or removes the stored tokens — a disconnect, a forced re-sync, a\n\t * reauthentication, or the connection set changing. A rejection names a specific credential, so once that\n\t * credential is gone the rejection describes nothing and must not force a refresh (or, for a deleted\n\t * connection, outlive it).\n\t *\n\t * Deliberately NOT conditioned on `_session`. A per-connection read resolves through the auth provider and\n\t * never populates the cached primary session, so the connections this recovery exists for are exactly the\n\t * ones with no `_session` to inspect — the same blind spot the recovery itself was added to fix. Callers\n\t * that guard on `_session` therefore invoke this ahead of that guard.\n\t */\n\tprotected onStoredTokensReplaced(): void {\n\t\tthis._rejectedTokens.clear();\n\t\tthis.invalidateDiscoveryCaches();\n\t}\n\n\t/**\n\t * Drops what this integration discovered through its session and kept for later reads: a tracker's sites,\n\t * projects or teams. Called by every path that replaces the stored tokens (see {@link onStoredTokensReplaced}),\n\t * which includes the forced re-sync a consumer's refresh runs, and by a read asked to `forceSync`. A refresh is\n\t * the consumer saying the account may have changed, and a project created or deleted since the first read would\n\t * otherwise stay invisible, or keep being searched, for the rest of the session (#5907).\n\t */\n\tinvalidateDiscoveryCaches(): void {}\n\n\tprivate static readonly requestExceptionLimit = 5;\n\tprivate requestExceptionCount = 0;\n\n\tresetRequestExceptionCount(syncReqUsecase: SyncReqUsecase | 'all'): void {\n\t\tthis.requestExceptionCount = 0;\n\t\tif (syncReqUsecase === 'all') {\n\t\t\tthis._syncRequestsPerFailedUsecase.clear();\n\t\t\t// 'all' is the whole-integration reset: a disconnect, or a re-sync that produced a new access token.\n\t\t\tthis.onStoredTokensReplaced();\n\t\t} else {\n\t\t\tthis._syncRequestsPerFailedUsecase.delete(syncReqUsecase);\n\t\t}\n\t}\n\n\t/**\n\t * Resets request exceptions without resetting the amount of syncs\n\t */\n\tsmoothifyRequestExceptionCount(): void {\n\t\t// On resync we reset exception count only to avoid infinitive syncs on failure\n\t\tthis.requestExceptionCount = 0;\n\t}\n\n\tasync reset(): Promise<void> {\n\t\tawait this.disconnect({ silent: true });\n\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\t}\n\n\t/**\n\t * Drops the in-memory session so the next access re-resolves it from storage. Used when the primary\n\t * connection changed underneath a warm integration (e.g. after `setPrimaryConnection`/\n\t * `deleteConnection`). Unlike {@link reset}/{@link disconnect}, it deletes nothing from storage.\n\t */\n\tswitchConnection(): void {\n\t\t// A connection was deleted, or a different one became primary. Ahead of the guard — see\n\t\t// `onStoredTokensReplaced`.\n\t\tthis.onStoredTokensReplaced();\n\n\t\tif (this._session === undefined) return;\n\n\t\tconst wasConnected = this._session != null;\n\t\tthis._session = undefined;\n\t\tthis._onDidChange.fire();\n\t\tvoid this.refreshAfterSwitch(wasConnected);\n\t}\n\n\tprivate async refreshAfterSwitch(wasConnected: boolean): Promise<void> {\n\t\tconst session = await this.ensureSession({ createIfNeeded: false });\n\t\tif (session != null || !wasConnected) return;\n\n\t\tthis._onDidChange.fire();\n\t\tthis.didChangeConnection?.fire({ integration: this, key: this.key, reason: 'disconnected' });\n\t\tawait this.providerOnDisconnect?.();\n\t}\n\n\tprivate skippedNonCloudReported = false;\n\t@debug()\n\tasync syncCloudConnection(state: 'connected' | 'disconnected', forceSync: boolean): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\t\t// Initially the condition on `this._session.cloud` has been added here: https://github.com/gitkraken/vscode-gitlens/commit/e95e70c430bd162924cc3bd5c1e8ab90e6293449#diff-4213141a45cccaab7aa2e40028b155a87eb913b07388485831403e60ce5555e4R237\n\t\t// I'm not sure about reasons, but it seems we want to replace it with the cloud session if it's connected.\n\t\t// Gradually we'll stop having non-cloud sessions.\n\t\t// However this is needed to be tested with PATs, e.g. with a GitLab PAT.\n\t\tif (this._session?.cloud === false && state !== 'connected') {\n\t\t\tif (this.id !== GitCloudHostIntegrationId.GitHub && !this.skippedNonCloudReported) {\n\t\t\t\tthis.ctx.hooks?.session?.onRefreshSkipped?.({\n\t\t\t\t\tid: this.id,\n\t\t\t\t\treason: 'skip-non-cloud',\n\t\t\t\t\tcloud: false,\n\t\t\t\t});\n\t\t\t\tthis.skippedNonCloudReported = true;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (state) {\n\t\t\tcase 'connected': {\n\t\t\t\tconst oldSession = this._session;\n\t\t\t\tlet resyncing = false;\n\t\t\t\tif (forceSync) {\n\t\t\t\t\t// Reset our stored session so that we get a new one from the cloud\n\t\t\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\t\t\tawait authProvider.deleteSession(this.authProviderDescriptor);\n\t\t\t\t\t// The stored token was just deleted. Not left to the token-changed check below, which needs\n\t\t\t\t\t// an `oldSession` — see `onStoredTokensReplaced`.\n\t\t\t\t\tthis.onStoredTokensReplaced();\n\t\t\t\t\t// Reset the session and clear our \"stay disconnected\" flag\n\t\t\t\t\tthis._session = undefined;\n\t\t\t\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\t\t\t\t\tresyncing = true;\n\t\t\t\t} else {\n\t\t\t\t\t// Only sync if we're not connected and not disabled and don't have pending errors\n\t\t\t\t\tif (\n\t\t\t\t\t\tthis._session != null ||\n\t\t\t\t\t\tthis.requestExceptionCount > 0 ||\n\t\t\t\t\t\tthis.ctx.storage.getWorkspace(this.connectedKey) === false\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tforceSync = true;\n\t\t\t\t}\n\n\t\t\t\t// sync option, rather than createIfNeeded, makes sure we don't call connectCloudIntegrations and open a gkdev window\n\t\t\t\t// if there was no session or some problem fetching/refreshing the existing session from the cloud api\n\t\t\t\tlet newSession: ProviderAuthenticationSession | undefined;\n\t\t\t\tlet refetchFailed = false;\n\t\t\t\ttry {\n\t\t\t\t\tnewSession = await this.ensureSession({ sync: forceSync });\n\t\t\t\t} catch (ex) {\n\t\t\t\t\t// Not evidence the connection is gone (#5569). Also leaves `_session` as-is rather than\n\t\t\t\t\t// latching null, so the next access can re-resolve it.\n\t\t\t\t\tif (!isCancellationError(ex)) {\n\t\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\t}\n\t\t\t\t\trefetchFailed = true;\n\t\t\t\t}\n\n\t\t\t\tif (oldSession && newSession && newSession.accessToken !== oldSession.accessToken) {\n\t\t\t\t\tthis.resetRequestExceptionCount('all');\n\t\t\t\t}\n\n\t\t\t\t// The forced re-sync above deleted the cloud secret but kept the descriptor to avoid UI churn.\n\t\t\t\t// Drop it only when the replacement fetch came back definitively empty, so a connection whose\n\t\t\t\t// token is really gone is cleanly disconnected (#5497) while a healthy one survives a blip (#5569).\n\t\t\t\tif (resyncing && newSession == null && !refetchFailed) {\n\t\t\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\t\t\tawait authProvider.deleteSession(this.authProviderDescriptor, { preserveConfigured: false });\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'disconnected':\n\t\t\t\tawait this.disconnect({ silent: true });\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Proves the credential with an uncached check, rejecting with the provider's `AuthenticationError` when it is\n\t * refused, and with anything else when the check proves nothing. A `403` proves nothing either: the credential\n\t * authenticated and was only denied the check's request.\n\t *\n\t * Implemented by the providers whose reads can reach their scopes without an uncached request to the connection\n\t * first: discovery served from a per-token cache (Azure DevOps, Bitbucket, Jira Cloud), or an SDK fan-out across\n\t * the requested repositories (Bitbucket Data Center). There, a dead token comes back as scoped refusals with no\n\t * failure of the connection: the same shape as one scope refusing a sound credential.\n\t * {@link confirmScopedAuthFailures} uses this to tell the two apart.\n\t */\n\tprotected validateCredential?(session: ProviderAuthenticationSession): Promise<void>;\n\n\t/**\n\t * Credentials that passed {@link validateCredential} within the last minute. A probe already in flight is\n\t * shared rather than repeated, and a refused one is not kept. Cleared whenever a credential is refused (see\n\t * {@link handleProviderException}), so a pass is never vouched for past a refusal this instance has seen.\n\t */\n\tprivate readonly _validatedCredentials = new PromiseCache<string, void>({ createTTL: 60 * 1000, capacity: 10 });\n\n\t/**\n\t * Whether a scope's refusal is the credential's own, by the provider's account of it: e.g. a token missing the\n\t * OAuth scopes the read needs. The probe cannot see that, because it proves the token authenticates, not that\n\t * it is authorized. So {@link confirmScopedAuthFailures} publishes one for the connection, unscoped, which asks\n\t * for the reconnect (consenting to the scopes again) that fixes it, and keeps the results of the scopes that\n\t * answered.\n\t */\n\tprotected isCredentialRefusal?(refusal: ProviderRefusal): boolean;\n\n\t/**\n\t * Names why a confirmed credential was refused by one scope, from what the refusal said (see\n\t * `ProviderWarning.cause`). Called only by {@link confirmScopedAuthFailures}, once the credential passed: the\n\t * refusals this names are indistinguishable from a dead credential until then.\n\t */\n\tprotected describeRefusal?(\n\t\tsession: ProviderAuthenticationSession,\n\t\trefusal: ProviderRefusal,\n\t\tscope: ProviderWarningScope,\n\t): ProviderWarningCause | undefined;\n\n\t/**\n\t * Settles what a read's only-scoped authentication failures mean, when the provider can tell (see\n\t * {@link validateCredential}).\n\t *\n\t * A refused credential is thrown: the caller's `catch` then fails the read as a whole, exactly as it does when\n\t * discovery is not cached and the first request is the one refused. Without this, a token revoked after\n\t * discovery would publish scoped `auth` warnings, which a consumer must not answer with a reconnect (see\n\t * `ProviderWarning.scope`), next to results served from the cache, for as long as the token stays stored.\n\t *\n\t * A confirmed credential makes each refusal the scope's own, so its cause is named through\n\t * {@link describeRefusal}. A probe that fails for another reason proves nothing either way and leaves the read\n\t * as it was.\n\t *\n\t * A refusal the provider pins on the credential itself (see {@link isCredentialRefusal}) is published for the\n\t * connection instead, without a probe.\n\t *\n\t * A token that passed within the last minute is not probed again, so a scope that keeps refusing it costs one\n\t * probe a minute, and a revocation right after a probe can take up to a minute to surface: until then its\n\t * refusals stay scoped, and may be named. A probe that proved nothing is not remembered, so the next read probes\n\t * again, and a credential no probe can confirm (e.g. a Bitbucket Data Center project access token) is probed on\n\t * every read that has scoped refusals.\n\t */\n\tprotected async confirmScopedAuthFailures(\n\t\tsession: ProviderAuthenticationSession,\n\t\tmetadata: CollectionMetadata | undefined,\n\t): Promise<void> {\n\t\tif (!hasOnlyScopedAuthFailures(metadata)) return;\n\n\t\t// Published for the connection, as a refusal outside any scope would be, while the scopes that answered keep\n\t\t// their results. That already asks for a reconnect, so there is nothing left for a probe to settle.\n\t\tif (this.markCredentialRefusals(metadata)) return;\n\t\tif ((await this.confirmCredential(session)) !== 'confirmed') return;\n\n\t\tthis.nameRefusalCauses(session, metadata);\n\t}\n\n\t/**\n\t * Settles what a batch read's authentication refusals mean before the call is judged, as\n\t * {@link confirmScopedAuthFailures} settles an account-wide read's: each refused slot comes back carrying the scope\n\t * failure it was recorded as, against the scope its target names (see {@link batchTargetScope}), and a read\n\t * publishes that instead of the bare reason.\n\t *\n\t * When any target answered, that proved the credential, so each refusal is its own target's. When every target\n\t * failed, the call fails as a whole, as it always has, unless every failure is a refusal of a credential that checks\n\t * out. Every target refusing is also what a dead credential looks like, so the credential is checked once first,\n\t * with {@link validateCredential}. A check that proves nothing leaves the refusals scoped and unnamed, as it leaves\n\t * an account-wide read's. A provider with no check cannot tell, and a refusal it pins on the credential (see\n\t * {@link isCredentialRefusal}) needs none, so either still fails the call.\n\t *\n\t * `keepOtherFailures` is for the etag cheap check, whose failed targets fall through to a full read one by one:\n\t * there, only a refused credential fails the call, always on one of the refusals, and every other failure stays\n\t * its own slot's. A batch with refusals and other failures but no answer then has its credential checked, as when\n\t * every target refused, and a provider with no check leaves its refusals scoped and unnamed.\n\t */\n\tprotected async settleBatchRefusals<T>(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly BatchTarget[],\n\t\tslots: PromiseSettledResult<T>[],\n\t\toptions?: { keepOtherFailures?: boolean },\n\t): Promise<BatchSlot<T>[]> {\n\t\tconst keepOtherFailures = options?.keepOtherFailures ?? false;\n\t\tconst failures = slots.map((slot, i) =>\n\t\t\tslot.status === 'rejected' && slot.reason instanceof AuthenticationError\n\t\t\t\t? toCollectionScopeFailure(batchTargetScope(this.id, targets[i]), slot.reason)\n\t\t\t\t: undefined,\n\t\t);\n\t\tconst refused = failures.filter(f => f != null);\n\t\tif (!refused.length) {\n\t\t\tif (!keepOtherFailures) {\n\t\t\t\tthrowIfAllSettledFailed(slots);\n\t\t\t}\n\t\t\treturn slots;\n\t\t}\n\n\t\tconst metadata: CollectionMetadata = { completeness: 'partial', failures: refused };\n\t\tconst credentialRefused = this.markCredentialRefusals(metadata);\n\t\tif (slots.some(slot => slot.status === 'fulfilled')) {\n\t\t\tthis.nameRefusalCauses(session, metadata);\n\t\t} else if (credentialRefused) {\n\t\t\tthrowIfAllSettledFailed(keepOtherFailures ? slots.filter((_, i) => failures[i] != null) : slots);\n\t\t} else if (refused.length < slots.length && !keepOtherFailures) {\n\t\t\t// Fails on a target that failed for another reason: failing on a refusal would run the credential's own\n\t\t\t// recovery (a session expiry or a disconnect strike) for what may be only its target's refusal.\n\t\t\tthrowIfAllSettledFailed(slots.filter((_, i) => failures[i] == null));\n\t\t} else {\n\t\t\tconst confirmation = await this.confirmCredential(session);\n\t\t\tif (confirmation == null) {\n\t\t\t\tif (refused.length === slots.length) {\n\t\t\t\t\tthrowIfAllSettledFailed(slots);\n\t\t\t\t}\n\t\t\t} else if (confirmation === 'confirmed') {\n\t\t\t\tthis.nameRefusalCauses(session, metadata);\n\t\t\t}\n\t\t}\n\n\t\treturn slots.map((slot, i) => {\n\t\t\tconst failure = failures[i];\n\t\t\treturn slot.status === 'rejected' && failure != null ? { ...slot, failure: failure } : slot;\n\t\t});\n\t}\n\n\t/**\n\t * Shows one notice, and spends at most one strike, for a batch read whose targets failed with a server error or a\n\t * timeout. Our own clients leave both to the batch (see `reportRequestFailure`) instead of spending them per\n\t * target. `strike` is true only when every target failed and the call's own failure spent none: a call that\n\t * answered some targets showed the host is up, and budgets strikes the same way as the rest of its failures.\n\t */\n\tprotected reportDeferredRequestFailures(\n\t\tslots: readonly PromiseSettledResult<unknown>[] | undefined,\n\t\tstrike: boolean,\n\t): void {\n\t\tlet notify: (() => void) | undefined;\n\t\tfor (const slot of slots ?? []) {\n\t\t\tif (slot.status !== 'rejected') continue;\n\n\t\t\tnotify = getDeferredRequestFailure(slot.reason);\n\t\t\tif (notify != null) break;\n\t\t}\n\n\t\tif (notify == null) return;\n\n\t\tif (strike) {\n\t\t\tthis.trackRequestException();\n\t\t}\n\t\tnotify();\n\t}\n\n\t/**\n\t * The failure handling of an etag cheap check that failed as a whole. Only a refused credential runs\n\t * {@link handleProviderException}, for its session expiry, re-authentication and strike. Anything else is only\n\t * logged, spending no strike and showing no notice: every target the check held then falls through to a full\n\t * read, which spends and notifies if the host really is failing, or is dropped, as rate-limited or without a\n\t * connection, with the check's warning. A rate limit costs nothing in `handleProviderException` either.\n\t */\n\tprotected handleEtagCheckException(\n\t\tsyncReqUsecase: SyncReqUsecase,\n\t\tex: Error,\n\t\toptions: { scope: ScopedLogger | undefined; connectionId: string | undefined },\n\t): void {\n\t\tif (ex instanceof AuthenticationError) {\n\t\t\tthis.handleProviderException(syncReqUsecase, ex, options);\n\t\t\treturn;\n\t\t}\n\t\tif (isCancellationError(ex)) return;\n\n\t\tif (options.scope != null) {\n\t\t\toptions.scope.error(ex);\n\t\t} else {\n\t\t\tLogger.error(ex);\n\t\t}\n\t}\n\n\t/** Marks the scoped refusals the provider pins on the credential (see {@link isCredentialRefusal}). */\n\tprivate markCredentialRefusals(metadata: CollectionMetadata | undefined): boolean {\n\t\tconst isCredentialRefusal = this.isCredentialRefusal?.bind(this);\n\t\treturn isCredentialRefusal != null && markCredentialRefusals(metadata, isCredentialRefusal);\n\t}\n\n\t/**\n\t * Checks the credential with {@link validateCredential}, rethrowing the provider's refusal when it is refused.\n\t * `undefined` when the provider has no check, and `'unproven'` when the check proved nothing either way.\n\t */\n\tprivate async confirmCredential(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<'confirmed' | 'unproven' | undefined> {\n\t\tconst validateCredential = this.validateCredential?.bind(this);\n\t\tif (validateCredential == null) return undefined;\n\n\t\t// Keyed by the address as well as the token: a self-managed instance serves every installation on its host,\n\t\t// and one installation accepting a token says nothing about another.\n\t\tconst key = [session.domain, session.baseUrl ?? '', session.accessToken].join('\\n');\n\t\ttry {\n\t\t\tawait this._validatedCredentials.getOrCreate(key, () => validateCredential(session));\n\t\t} catch (ex) {\n\t\t\t// A 403 answered a credential that authenticated and was only denied the check's own request, so it proves\n\t\t\t// nothing about the scopes' refusals either.\n\t\t\tif (ex instanceof AuthenticationError && ex.reason !== AuthenticationErrorReason.Forbidden) throw ex;\n\n\t\t\treturn 'unproven';\n\t\t}\n\t\treturn 'confirmed';\n\t}\n\n\t/** Names each scoped refusal's cause (see {@link describeRefusal}); only once the credential is proven. */\n\tprivate nameRefusalCauses(session: ProviderAuthenticationSession, metadata: CollectionMetadata | undefined): void {\n\t\tconst describeRefusal = this.describeRefusal?.bind(this);\n\t\tif (describeRefusal == null) return;\n\n\t\tattributeScopedAuthFailures(metadata, (refusal, scope) => describeRefusal(session, refusal, scope));\n\t}\n\n\t/** Returns whether it spent a strike toward disconnecting (see {@link trackRequestException}). */\n\tprotected handleProviderException(\n\t\tsyncReqUsecase: SyncReqUsecase,\n\t\tex: Error,\n\t\toptions?: { scope?: ScopedLogger | undefined; silent?: boolean; connectionId?: string },\n\t): boolean {\n\t\tif (isCancellationError(ex)) return false;\n\n\t\t// A refused credential may be one a probe passed moments ago: stop vouching for it.\n\t\tif (ex instanceof AuthenticationError) {\n\t\t\tthis._validatedCredentials.clear();\n\t\t}\n\n\t\tif (options?.scope != null) {\n\t\t\toptions.scope.error(ex);\n\t\t} else {\n\t\t\tLogger.error(ex);\n\t\t}\n\n\t\t// A per-connection (multi-account) read resolved its session through `resolveReadSession`'s\n\t\t// `connectionId` branch, which deliberately never touches the cached primary `_session`. So the\n\t\t// primary-session recovery below cannot apply to it: expiring `_session` would mark a session this\n\t\t// read never used, while the rejected connection kept its stored token and re-sent it on every\n\t\t// later read — a token the provider has already refused (expired scopes, a revoked grant, an\n\t\t// uninstalled app) is not self-healing, so the read failed identically until the user reconnected\n\t\t// by hand. Record the rejection against the connection instead; `resolveReadSession` consumes it\n\t\t// and forces the cloud `/refresh` on the next read of that connection. When there is nothing to\n\t\t// recover, fall through to the shared failure budget, which disconnects after\n\t\t// `requestExceptionLimit` and surfaces the reconnect prompt.\n\t\tif (ex instanceof AuthenticationError && options?.connectionId) {\n\t\t\tif (this._rejectedTokens.recordRejection(options.connectionId)) return false;\n\n\t\t\tthis.trackRequestException(options);\n\t\t\treturn true;\n\t\t}\n\n\t\tif (ex instanceof AuthenticationError && this._session?.cloud && !this.hasSessionSyncRequests()) {\n\t\t\tthis.requestSessionSyncForUsecase(syncReqUsecase);\n\t\t\tthis._session = {\n\t\t\t\t...this._session,\n\t\t\t\texpiresAt: new Date(Date.now() - 1),\n\t\t\t};\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!(ex instanceof AuthenticationError) && !(ex instanceof RequestClientError)) return false;\n\n\t\tthis.trackRequestException(options);\n\t\treturn true;\n\t}\n\n\tprivate missingExpirityReported = false;\n\t@gate()\n\tprotected async refreshSessionIfExpired(scope?: ScopedLogger): Promise<void> {\n\t\tif (this._session?.expiresAt != null && this._session.expiresAt < new Date()) {\n\t\t\t// The current session is expired, so get the latest from the cloud and refresh if needed\n\t\t\ttry {\n\t\t\t\tawait this.syncCloudConnection('connected', true);\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex);\n\t\t\t}\n\t\t} else if (\n\t\t\tthis._session?.expiresAt == null &&\n\t\t\tthis.id !== GitCloudHostIntegrationId.GitHub &&\n\t\t\t!this.missingExpirityReported\n\t\t) {\n\t\t\tthis.ctx.hooks?.session?.onRefreshSkipped?.({\n\t\t\t\tid: this.id,\n\t\t\t\treason: 'missing-expiry',\n\t\t\t\tcloud: this._session?.cloud,\n\t\t\t});\n\t\t\tthis.missingExpirityReported = true;\n\t\t}\n\t}\n\n\t@trace()\n\ttrackRequestException(options?: { silent?: boolean }): void {\n\t\tthis.requestExceptionCount++;\n\n\t\tif (this.requestExceptionCount >= IntegrationBase.requestExceptionLimit && this._session !== null) {\n\t\t\tif (!options?.silent) {\n\t\t\t\tthis.ctx.hooks?.ui?.onDisconnectedAfterTooManyFailures?.(this.name);\n\t\t\t}\n\t\t\tvoid this.disconnect({ currentSessionOnly: true });\n\t\t}\n\t}\n\n\t@gate()\n\t@trace({ exit: true })\n\tasync isConnected(): Promise<boolean> {\n\t\treturn (await this.getSession('integrations')) != null;\n\t}\n\n\t@gate()\n\tprivate async ensureSession(\n\t\toptions:\n\t\t\t| {\n\t\t\t\t\tcreateIfNeeded?: boolean;\n\t\t\t\t\tforceNewSession?: boolean;\n\t\t\t\t\tsync?: never;\n\t\t\t\t\tsource?: Sources;\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tcreateIfNeeded?: never;\n\t\t\t\t\tforceNewSession?: never;\n\t\t\t\t\tsync: boolean;\n\t\t\t\t\tsource?: Sources;\n\t\t\t },\n\t): Promise<ProviderAuthenticationSession | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst { createIfNeeded, forceNewSession, source, sync } = options;\n\t\tif (this._session != null) {\n\t\t\tif (this.isSessionForIntegrationHost(this._session)) return this._session;\n\n\t\t\tthis._session = null;\n\t\t}\n\t\tif (this.ctx.config.isIntegrationsEnabled?.() === false) return undefined;\n\n\t\tif (createIfNeeded || sync) {\n\t\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\t\t} else if (this.ctx.storage.getWorkspace(this.connectedKey) === false) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet session: ProviderAuthenticationSession | undefined | null;\n\t\ttry {\n\t\t\tconst authProvider = await this.authenticationService.get(this.authProvider.id);\n\t\t\tsession = await authProvider.getSession(\n\t\t\t\tthis.authProviderDescriptor,\n\t\t\t\tsync\n\t\t\t\t\t? { sync: sync, source: source }\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tcreateIfNeeded: createIfNeeded,\n\t\t\t\t\t\t\tforceNewSession: forceNewSession,\n\t\t\t\t\t\t\tsource: source,\n\t\t\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (session?.expiresAt != null && session.expiresAt < new Date()) {\n\t\t\t\tsession = null;\n\t\t\t}\n\t\t\tif (session != null && !this.isSessionForIntegrationHost(session)) {\n\t\t\t\tsession = undefined;\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\n\t\t\t// Only interactive paths can prompt for consent, so a sync failure must reach the rethrow below.\n\t\t\tif (!sync && ex instanceof Error && ex.message.includes('User did not consent')) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// On a forced re-sync, propagate so syncCloudConnection can tell a failure from a definitive empty\n\t\t\t// result (#5569); other callers keep swallowing to null so reads never throw.\n\t\t\tif (sync) {\n\t\t\t\t// Throwing skips the reset below, and a lingering count blocks the next non-forced sync.\n\t\t\t\tthis.smoothifyRequestExceptionCount();\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\tsession = null;\n\t\t}\n\n\t\tif (session === undefined && !createIfNeeded && !sync) {\n\t\t\tawait this.ctx.storage.deleteWorkspace(this.connectedKey);\n\t\t}\n\n\t\tthis._session = session ?? null;\n\t\tthis.smoothifyRequestExceptionCount();\n\n\t\tif (session != null) {\n\t\t\tawait this.ctx.storage.storeWorkspace(this.connectedKey, true);\n\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tthis._onDidChange.fire();\n\t\t\t\tthis.didChangeConnection?.fire({ integration: this, key: this.key, reason: 'connected' });\n\t\t\t\t// Fired detached, so there is no caller left to catch anything: every implementor is async, and\n\t\t\t\t// a rejection would surface as a process-level unhandled rejection in the host. It is a\n\t\t\t\t// best-effort warm-up, so swallow the failure with a warning instead.\n\t\t\t\tvoid (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.providerOnConnect?.();\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tscope?.warn(\n\t\t\t\t\t\t\t`Failed to run providerOnConnect for ${this.key}: ${ex instanceof Error ? ex.message : String(ex)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t});\n\t\t}\n\n\t\treturn session ?? undefined;\n\t}\n\n\tgetIgnoreSSLErrors(): boolean | 'force' {\n\t\treturn this.authenticationService.ignoreSSLErrors(this);\n\t}\n\n\tasync searchMyIssues(\n\t\tresource?: ResourceDescriptor,\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined>;\n\tasync searchMyIssues(\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined>;\n\t@trace()\n\tasync searchMyIssues(\n\t\tresources?: ResourceDescriptor | ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchMyIssuesResult(resources, cancellation, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link searchMyIssues}. Recovers thrown errors into `{ error }` so callers\n\t * (e.g. the ProviderBackend account-wide issues read) can surface a per-provider warning instead of a\n\t * silent empty result. Returns the normalized {@link IssueShape} (there is no raw account-wide issue read).\n\t */\n\tasync searchMyIssuesResult(\n\t\tresources?: ResourceDescriptor | ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<IssueShape[] | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tconst issues = await this.searchProviderMyIssues(\n\t\t\t\tsession,\n\t\t\t\tresources != null ? (Array.isArray(resources) ? resources : [resources]) : undefined,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tthis.resetRequestExceptionCount('searchMyIssues');\n\t\t\treturn { value: issues, duration: performance.now() - start };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('searchMyIssues', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex), duration: performance.now() - start };\n\t\t}\n\t}\n\n\tprotected abstract searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined>;\n\n\t/**\n\t * Paging/truncation-aware variant of {@link searchProviderMyIssues}. The default wraps the normalized read\n\t * as a complete single page; providers with native cursors or fan-out metadata override it.\n\t */\n\tprotected async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\t_options?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\t// The default read has no assignee scoping to broaden, so `_options` is inert here; a provider whose\n\t\t// account-wide read is user-scoped (GitHub/GitLab/Azure) overrides this and honors `includeAllAssignees`.\n\t\tconst values = await this.searchProviderMyIssues(session, resources, cancellation);\n\t\tif (values == null) return undefined;\n\t\treturn { values: values, truncated: false };\n\t}\n\n\t/**\n\t * Result-returning, truncation-aware account-wide issue read. Recovers thrown errors into `{ error }` and\n\t * carries the `truncated` flag so the ProviderBackend facade can report an incomplete read honestly.\n\t */\n\tasync searchMyIssuesWithTruncationResult(\n\t\tresources?: ResourceDescriptor | ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\tconnectionId?: string,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<IntegrationResult<AccountWideIssuesResult | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst result = await this.searchProviderMyIssuesWithTruncation(\n\t\t\t\tsession,\n\t\t\t\tresources != null ? (Array.isArray(resources) ? resources : [resources]) : undefined,\n\t\t\t\tcancellation,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tawait this.confirmScopedAuthFailures(session, result?.metadata);\n\t\t\tthis.resetRequestExceptionCount('searchMyIssues');\n\t\t\treturn { value: result };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('searchMyIssues', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t@trace()\n\tasync getLinkedIssueOrPullRequest(\n\t\tresource: T,\n\t\tlink: { id: string; key: string },\n\t\toptions?: { expiryOverride?: boolean | number; type?: IssueOrPullRequestType },\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected) return undefined;\n\n\t\tawait this.refreshSessionIfExpired(scope);\n\n\t\tconst issueOrPR = this.ctx.cache.getIssueOrPullRequest(\n\t\t\tlink.key,\n\t\t\toptions?.type,\n\t\t\tresource,\n\t\t\tthis,\n\t\t\t() => ({\n\t\t\t\tvalue: (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await this.getProviderLinkedIssueOrPullRequest(\n\t\t\t\t\t\t\tthis._session!,\n\t\t\t\t\t\t\tresource,\n\t\t\t\t\t\t\tlink,\n\t\t\t\t\t\t\toptions?.type,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.resetRequestExceptionCount('getIssueOrPullRequest');\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tthis.handleProviderException('getIssueOrPullRequest', ex, { scope: scope });\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t})(),\n\t\t\t}),\n\t\t\toptions,\n\t\t);\n\t\treturn issueOrPR;\n\t}\n\n\tprotected abstract getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t\tlink: { id: string; key: string },\n\t\ttype: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined>;\n\n\tasync getIssue(\n\t\tresource: T,\n\t\tid: string,\n\t\toptions?: { connectionId?: string; expiryOverride?: boolean | number },\n\t): Promise<Issue | undefined> {\n\t\treturn (await this.getIssueResult(resource, id, options))?.value;\n\t}\n\n\tgetIssueResult(\n\t\tresource: T,\n\t\tid: string,\n\t\toptions?: { connectionId?: string; expiryOverride?: boolean | number },\n\t): Promise<IntegrationResult<Issue | undefined>> {\n\t\treturn this.getIssueResultCore(resource, id, options, session => this.getProviderIssue(session, resource, id));\n\t}\n\n\t@trace()\n\tprotected async getIssueResultCore(\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t\toptions: { connectionId?: string; expiryOverride?: boolean | number } | undefined,\n\t\tgetProviderIssue: (session: ProviderAuthenticationSession) => Promise<Issue | undefined>,\n\t): Promise<IntegrationResult<Issue | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { connectionId: requestedConnectionId, expiryOverride } = options ?? {};\n\t\tconst connectionId = requestedConnectionId || undefined;\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst issue = await getCachedIssue({\n\t\t\t\tcache: this.ctx.cache,\n\t\t\t\tid: id,\n\t\t\t\tresource: resource,\n\t\t\t\tintegration: this,\n\t\t\t\tload: async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst issue = await getProviderIssue(session);\n\t\t\t\t\t\tthis.resetRequestExceptionCount('getIssue');\n\t\t\t\t\t\treturn issue;\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tif (!isCancellationError(ex)) {\n\t\t\t\t\t\t\tthis.handleProviderException('getIssue', toError(ex), {\n\t\t\t\t\t\t\t\tscope: scope,\n\t\t\t\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tcacheOptions: {\n\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t\texpiryOverride: expiryOverride,\n\t\t\t\t\tetag: `${this.id}:${this.maybeConnected ?? false}:${this.getSessionFingerprint(session)}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn { value: issue };\n\t\t} catch (ex) {\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t\tid: string,\n\t): Promise<Issue | undefined>;\n\n\tasync getCurrentAccount(options?: {\n\t\tavatarSize?: number;\n\t\tconnectionId?: string;\n\t\texpiryOverride?: boolean | number;\n\t}): Promise<Account | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { connectionId: requestedConnectionId, expiryOverride, ...opts } = options ?? {};\n\t\tconst connectionId = requestedConnectionId || undefined;\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\tconst sessionFingerprint = this.getSessionFingerprint(session);\n\n\t\tconst currentAccount = await this.ctx.cache.getCurrentAccount(\n\t\t\tthis,\n\n\t\t\t(cacheable: any) => ({\n\t\t\t\tvalue: (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst account = await this.getProviderCurrentAccount?.(session, opts);\n\t\t\t\t\t\tthis.resetRequestExceptionCount('getCurrentAccount');\n\t\t\t\t\t\treturn account;\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\tif (isCancellationError(ex)) {\n\t\t\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.handleProviderException('getCurrentAccount', ex, {\n\t\t\t\t\t\t\tscope: scope,\n\t\t\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Invalidate the cache on error, except for auth errors\n\t\t\t\t\t\tif (!(ex instanceof AuthenticationError)) {\n\t\t\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Re-throw to the caller\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t})(),\n\t\t\t}),\n\t\t\t{\n\t\t\t\tconnectionId: connectionId,\n\t\t\t\texpiryOverride: expiryOverride,\n\t\t\t\texpireOnError: false,\n\t\t\t\tetag: `${this.id}:${this.maybeConnected ?? false}:${sessionFingerprint}`,\n\t\t\t},\n\t\t);\n\t\treturn currentAccount;\n\t}\n\n\tprotected getProviderCurrentAccount?(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: { avatarSize?: number },\n\t): Promise<Account | undefined>;\n\n\t/** Whether this integration can answer {@link getCurrentAccount} at all — Jira, Linear and Trello cannot. */\n\tget supportsCurrentAccount(): boolean {\n\t\treturn this.getProviderCurrentAccount != null;\n\t}\n\n\t/**\n\t * Resolves the account for a specific session/token — including connections other than the current\n\t * primary (multi-account) — using this integration's provider API base URL and auth type. Returns\n\t * undefined when the provider doesn't support account lookup. Uncached (callers cache per connection).\n\t */\n\tgetProviderAccountForSession(session: ProviderAuthenticationSession): Promise<Account | undefined> {\n\t\treturn this.getProviderCurrentAccount?.(session) ?? Promise.resolve(undefined);\n\t}\n\n\t@trace()\n\tasync getPullRequest(\n\t\tresource: T,\n\t\tid: string,\n\t\toptions?: { expiryOverride?: boolean | number; throwOnError?: boolean },\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected) return undefined;\n\n\t\tawait this.refreshSessionIfExpired(scope);\n\n\t\tconst pr = await this.ctx.cache.getPullRequest(\n\t\t\tid,\n\t\t\tresource,\n\t\t\tthis,\n\t\t\tcacheable => ({\n\t\t\t\tvalue: (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await this.getProviderPullRequest?.(this._session!, resource, id);\n\t\t\t\t\t\tthis.resetRequestExceptionCount('getPullRequest');\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t// A failed lookup is not an answer — and the by-id bucket never expires a miss.\n\t\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t\t\tthis.handleProviderException('getPullRequest', ex, { scope: scope });\n\t\t\t\t\t\tif (options?.throwOnError) throw ex;\n\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t})(),\n\t\t\t}),\n\t\t\t{ expiryOverride: options?.expiryOverride },\n\t\t);\n\t\treturn pr;\n\t}\n\n\tprotected getProviderPullRequest?(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t\tid: string,\n\t): Promise<PullRequest | undefined>;\n}\n"],"mappings":"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"}