@gitkraken/core-gitlens 0.9.9 → 0.10.1

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 (53) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/dist/git/errors.d.ts +7 -0
  3. package/dist/git/errors.js +14 -1
  4. package/dist/git/errors.js.map +1 -1
  5. package/dist/git/providers/status.d.ts +6 -1
  6. package/dist/git/run.types.d.ts +9 -4
  7. package/dist/git-cli/exec/git.js +36 -16
  8. package/dist/git-cli/exec/git.js.map +1 -1
  9. package/dist/git-cli/providers/diff.d.ts +5 -2
  10. package/dist/git-cli/providers/diff.js +21 -9
  11. package/dist/git-cli/providers/diff.js.map +1 -1
  12. package/dist/git-cli/providers/patch.js +5 -7
  13. package/dist/git-cli/providers/patch.js.map +1 -1
  14. package/dist/git-cli/providers/status.d.ts +0 -1
  15. package/dist/git-cli/providers/status.js +119 -142
  16. package/dist/git-cli/providers/status.js.map +1 -1
  17. package/dist/plus/git-github/providers/github/status.d.ts +0 -1
  18. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  19. package/dist/plus/integrations/authentication/cloudIntegrationService.js +24 -7
  20. package/dist/plus/integrations/authentication/cloudIntegrationService.js.map +1 -1
  21. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts +7 -0
  22. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +13 -3
  23. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js.map +1 -1
  24. package/dist/plus/integrations/integrationService.js +4 -2
  25. package/dist/plus/integrations/integrationService.js.map +1 -1
  26. package/dist/plus/integrations/models/gitHostIntegration.js +20 -20
  27. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  28. package/dist/plus/integrations/models/integration.d.ts +29 -3
  29. package/dist/plus/integrations/models/integration.js +53 -12
  30. package/dist/plus/integrations/models/integration.js.map +1 -1
  31. package/dist/plus/integrations/models/issuesIntegration.js +9 -9
  32. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  33. package/dist/plus/integrations/providers/azureDevOps.js +1 -1
  34. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  35. package/dist/plus/integrations/providers/azureDevOpsServer.js +1 -1
  36. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  37. package/dist/plus/integrations/providers/bitbucket-server.js +1 -1
  38. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  39. package/dist/plus/integrations/providers/bitbucket.js +1 -1
  40. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  41. package/dist/plus/integrations/providers/github.js +2 -2
  42. package/dist/plus/integrations/providers/github.js.map +1 -1
  43. package/dist/plus/integrations/providers/gitlab.js +1 -1
  44. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  45. package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
  46. package/dist/plus/integrations/reads/drains.d.ts +1 -0
  47. package/dist/utils/cancellation.d.ts +4 -1
  48. package/dist/utils/cancellation.js +4 -2
  49. package/dist/utils/cancellation.js.map +1 -1
  50. package/dist/utils/env/node/exec.d.ts +5 -1
  51. package/dist/utils/env/node/exec.js +8 -5
  52. package/dist/utils/env/node/exec.js.map +1 -1
  53. package/package.json +1 -1
@@ -37,6 +37,12 @@ export type IntegrationResult<T> = {
37
37
  duration?: number;
38
38
  value?: never;
39
39
  } | undefined;
40
+ /** A per-connection read's session that could not be resolved for a retryable reason (see `resolveReadSession`). */
41
+ export declare class ReadSessionFailure {
42
+ readonly error: Error;
43
+ constructor(error: Error);
44
+ }
45
+ export declare function isReadSessionFailure(session: ProviderAuthenticationSession | ReadSessionFailure | undefined): session is ReadSessionFailure;
40
46
  /**
41
47
  * One target's answer in a batch read. A slot rejected by an authentication refusal carries the scope failure it was
42
48
  * recorded as (see `IntegrationBase.settleBatchRefusals`).
@@ -158,6 +164,12 @@ export declare abstract class IntegrationBase<ID extends IntegrationIds = Integr
158
164
  protected getSessionFingerprint(session: ProviderAuthenticationSession): string;
159
165
  get connectionExpired(): boolean | undefined;
160
166
  protected _session: ProviderAuthenticationSession | null | undefined;
167
+ /**
168
+ * Bumped whenever a forced re-sync's refetch settles `_session`. A forced re-sync keeps the stored token while it
169
+ * fetches the replacement, so a read that resolved that token concurrently must not publish it over whatever the
170
+ * refetch settled on: a fresh token, or nothing after the cloud said the connection is gone.
171
+ */
172
+ private _sessionGeneration;
161
173
  getSession(source: Sources): ProviderAuthenticationSession | Promise<ProviderAuthenticationSession | undefined> | undefined;
162
174
  private isSessionForIntegrationHost;
163
175
  /**
@@ -165,10 +177,24 @@ export declare abstract class IntegrationBase<ID extends IntegrationIds = Integr
165
177
  * omitted this is the integration's primary session, resolved exactly like the existing read flow
166
178
  * (ensure-connected + refresh-if-expired). When set, it resolves THAT connection's session directly
167
179
  * from the auth provider — refreshing it if expired — WITHOUT disturbing the cached primary
168
- * `_session`. Returns undefined when the requested session can't be resolved (e.g. the connection is
169
- * gone or the provider isn't connected), so callers degrade to "no results".
180
+ * `_session`. Never throws: `undefined` when there is no session to read with (the connection is gone,
181
+ * the provider isn't connected), `{ error }` when the connection's token could not be fetched.
182
+ *
183
+ * The two must not be conflated. A token fetch that THROWS is, by `getConnectionSession`'s contract, a
184
+ * retryable failure (a rate-limited or failing GK API, a network error), not evidence that the connection is
185
+ * gone (#5569). Answering "no session" for it made every connection read during a GK API 429 report
186
+ * `no-connection`, which a consumer rightly treats as a credential to reconnect (gitkraken/kepler#3546).
187
+ * Returned as the read's error instead, the read core surfaces it like any other failed request: a rate
188
+ * limit as `rate-limit`, anything else as `other`.
189
+ */
190
+ protected resolveReadSession(connectionId: string | undefined, scope: ScopedLogger | undefined, source?: Sources): Promise<ProviderAuthenticationSession | ReadSessionFailure | undefined>;
191
+ /**
192
+ * {@link resolveReadSession} for a read that already rethrows its provider's failures (a repository or account
193
+ * lookup), or whose callers treat any failure as "nothing to show": a token that could not be fetched is
194
+ * thrown like those failures, so it is classified as the failed request it is rather than as a missing
195
+ * connection.
170
196
  */
171
- protected resolveReadSession(connectionId: string | undefined, scope: ScopedLogger | undefined, source?: Sources): Promise<ProviderAuthenticationSession | undefined>;
197
+ protected resolveReadSessionOrThrow(connectionId: string | undefined, scope: ScopedLogger | undefined): Promise<ProviderAuthenticationSession | undefined>;
172
198
  connect(source: Sources): Promise<boolean>;
173
199
  protected providerOnConnect?(): void | Promise<void>;
174
200
  disconnect(options?: {
@@ -19,6 +19,16 @@ 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
+ /** A per-connection read's session that could not be resolved for a retryable reason (see `resolveReadSession`). */
23
+ var ReadSessionFailure = class {
24
+ error;
25
+ constructor(error) {
26
+ this.error = error;
27
+ }
28
+ };
29
+ function isReadSessionFailure(session) {
30
+ return session instanceof ReadSessionFailure;
31
+ }
22
32
  /** Every {@link PullRequestEtagInclude}, in the canonical order an etag lists them in. */
23
33
  const pullRequestEtagIncludes = [
24
34
  "mergeable",
@@ -133,6 +143,12 @@ var IntegrationBase = class IntegrationBase {
133
143
  return new Date(this._session.expiresAt) < /* @__PURE__ */ new Date();
134
144
  }
135
145
  _session;
146
+ /**
147
+ * Bumped whenever a forced re-sync's refetch settles `_session`. A forced re-sync keeps the stored token while it
148
+ * fetches the replacement, so a read that resolved that token concurrently must not publish it over whatever the
149
+ * refetch settled on: a fresh token, or nothing after the cloud said the connection is gone.
150
+ */
151
+ _sessionGeneration = 0;
136
152
  getSession(source) {
137
153
  if (this._session === void 0) return this.ensureSession({
138
154
  createIfNeeded: false,
@@ -149,8 +165,15 @@ var IntegrationBase = class IntegrationBase {
149
165
  * omitted this is the integration's primary session, resolved exactly like the existing read flow
150
166
  * (ensure-connected + refresh-if-expired). When set, it resolves THAT connection's session directly
151
167
  * from the auth provider — refreshing it if expired — WITHOUT disturbing the cached primary
152
- * `_session`. Returns undefined when the requested session can't be resolved (e.g. the connection is
153
- * gone or the provider isn't connected), so callers degrade to "no results".
168
+ * `_session`. Never throws: `undefined` when there is no session to read with (the connection is gone,
169
+ * the provider isn't connected), `{ error }` when the connection's token could not be fetched.
170
+ *
171
+ * The two must not be conflated. A token fetch that THROWS is, by `getConnectionSession`'s contract, a
172
+ * retryable failure (a rate-limited or failing GK API, a network error), not evidence that the connection is
173
+ * gone (#5569). Answering "no session" for it made every connection read during a GK API 429 report
174
+ * `no-connection`, which a consumer rightly treats as a credential to reconnect (gitkraken/kepler#3546).
175
+ * Returned as the read's error instead, the read core surfaces it like any other failed request: a rate
176
+ * limit as `rate-limit`, anything else as `other`.
154
177
  */
155
178
  async resolveReadSession(connectionId, scope, source) {
156
179
  if (this.ctx.config.isIntegrationsEnabled?.() === false || this.ctx.storage.getWorkspace(this.connectedKey) === false) return;
@@ -167,14 +190,26 @@ var IntegrationBase = class IntegrationBase {
167
190
  });
168
191
  return session != null && this.isSessionForIntegrationHost(session) ? session : void 0;
169
192
  } catch (ex) {
193
+ if (isCancellationError(ex)) return void 0;
170
194
  scope?.error(ex);
171
- return;
195
+ return new ReadSessionFailure(toError(ex));
172
196
  }
173
197
  }
174
198
  if (!(this.maybeConnected ?? await this.isConnected())) return void 0;
175
199
  await this.refreshSessionIfExpired(scope);
176
200
  return this._session != null && this.isSessionForIntegrationHost(this._session) ? this._session : void 0;
177
201
  }
202
+ /**
203
+ * {@link resolveReadSession} for a read that already rethrows its provider's failures (a repository or account
204
+ * lookup), or whose callers treat any failure as "nothing to show": a token that could not be fetched is
205
+ * thrown like those failures, so it is classified as the failed request it is rather than as a missing
206
+ * connection.
207
+ */
208
+ async resolveReadSessionOrThrow(connectionId, scope) {
209
+ const session = await this.resolveReadSession(connectionId, scope);
210
+ if (isReadSessionFailure(session)) throw session.error;
211
+ return session;
212
+ }
178
213
  async connect(source) {
179
214
  try {
180
215
  return Boolean(await this.ensureSession({
@@ -321,7 +356,6 @@ var IntegrationBase = class IntegrationBase {
321
356
  const oldSession = this._session;
322
357
  let resyncing = false;
323
358
  if (forceSync) {
324
- await (await this.authenticationService.get(this.authProvider.id)).deleteSession(this.authProviderDescriptor);
325
359
  this.onStoredTokensReplaced();
326
360
  this._session = void 0;
327
361
  await this.ctx.storage.deleteWorkspace(this.connectedKey);
@@ -333,7 +367,10 @@ var IntegrationBase = class IntegrationBase {
333
367
  let newSession;
334
368
  let refetchFailed = false;
335
369
  try {
336
- newSession = await this.ensureSession({ sync: forceSync });
370
+ newSession = await this.ensureSession({
371
+ sync: forceSync,
372
+ refetch: resyncing
373
+ });
337
374
  } catch (ex) {
338
375
  if (!isCancellationError(ex)) scope?.error(ex);
339
376
  refetchFailed = true;
@@ -545,18 +582,20 @@ var IntegrationBase = class IntegrationBase {
545
582
  }
546
583
  async ensureSession(options) {
547
584
  const scope = getScopedLogger();
548
- const { createIfNeeded, forceNewSession, source, sync } = options;
549
- if (this._session != null) {
585
+ const { createIfNeeded, forceNewSession, source, sync, refetch } = options;
586
+ if (this._session != null && !refetch) {
550
587
  if (this.isSessionForIntegrationHost(this._session)) return this._session;
551
588
  this._session = null;
552
589
  }
553
590
  if (this.ctx.config.isIntegrationsEnabled?.() === false) return void 0;
554
591
  if (createIfNeeded || sync) await this.ctx.storage.deleteWorkspace(this.connectedKey);
555
592
  else if (this.ctx.storage.getWorkspace(this.connectedKey) === false) return;
593
+ const generation = this._sessionGeneration;
556
594
  let session;
557
595
  try {
558
596
  session = await (await this.authenticationService.get(this.authProvider.id)).getSession(this.authProviderDescriptor, sync ? {
559
597
  sync,
598
+ refetch,
560
599
  source
561
600
  } : {
562
601
  createIfNeeded,
@@ -574,6 +613,8 @@ var IntegrationBase = class IntegrationBase {
574
613
  }
575
614
  session = null;
576
615
  }
616
+ if (refetch) this._sessionGeneration++;
617
+ else if (generation !== this._sessionGeneration) return this._session ?? void 0;
577
618
  if (session === void 0 && !createIfNeeded && !sync) await this.ctx.storage.deleteWorkspace(this.connectedKey);
578
619
  this._session = session ?? null;
579
620
  this.smoothifyRequestExceptionCount();
@@ -611,7 +652,7 @@ var IntegrationBase = class IntegrationBase {
611
652
  async searchMyIssuesResult(resources, cancellation, connectionId) {
612
653
  const scope = getScopedLogger();
613
654
  const session = await this.resolveReadSession(connectionId, scope);
614
- if (session == null) return void 0;
655
+ if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
615
656
  const start = performance.now();
616
657
  try {
617
658
  const issues = await this.searchProviderMyIssues(session, resources != null ? Array.isArray(resources) ? resources : [resources] : void 0, cancellation);
@@ -650,7 +691,7 @@ var IntegrationBase = class IntegrationBase {
650
691
  async searchMyIssuesWithTruncationResult(resources, cancellation, connectionId, options) {
651
692
  const scope = getScopedLogger();
652
693
  const session = await this.resolveReadSession(connectionId, scope);
653
- if (session == null) return void 0;
694
+ if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
654
695
  try {
655
696
  const result = await this.searchProviderMyIssuesWithTruncation(session, resources != null ? Array.isArray(resources) ? resources : [resources] : void 0, cancellation, options);
656
697
  await this.confirmScopedAuthFailures(session, result?.metadata);
@@ -690,7 +731,7 @@ var IntegrationBase = class IntegrationBase {
690
731
  const { connectionId: requestedConnectionId, expiryOverride } = options ?? {};
691
732
  const connectionId = requestedConnectionId || void 0;
692
733
  const session = await this.resolveReadSession(connectionId, scope);
693
- if (session == null) return void 0;
734
+ if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
694
735
  try {
695
736
  return { value: await getCachedIssue({
696
737
  cache: this.ctx.cache,
@@ -724,7 +765,7 @@ var IntegrationBase = class IntegrationBase {
724
765
  const scope = getScopedLogger();
725
766
  const { connectionId: requestedConnectionId, expiryOverride, ...opts } = options ?? {};
726
767
  const connectionId = requestedConnectionId || void 0;
727
- const session = await this.resolveReadSession(connectionId, scope);
768
+ const session = await this.resolveReadSessionOrThrow(connectionId, scope);
728
769
  if (session == null) return void 0;
729
770
  const sessionFingerprint = this.getSessionFingerprint(session);
730
771
  return await this.ctx.cache.getCurrentAccount(this, (cacheable) => ({ value: (async () => {
@@ -794,6 +835,6 @@ __decorate([trace()], IntegrationBase.prototype, "getLinkedIssueOrPullRequest",
794
835
  __decorate([trace()], IntegrationBase.prototype, "getIssueResultCore", null);
795
836
  __decorate([trace()], IntegrationBase.prototype, "getPullRequest", null);
796
837
  //#endregion
797
- export { IntegrationBase, issueEtagIncludes, pullRequestEtagIncludes, pullRequestRevision };
838
+ export { IntegrationBase, ReadSessionFailure, isReadSessionFailure, issueEtagIncludes, pullRequestEtagIncludes, pullRequestRevision };
798
839
 
799
840
  //# 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 {\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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"}
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/** A per-connection read's session that could not be resolved for a retryable reason (see `resolveReadSession`). */\nexport class ReadSessionFailure {\n\tconstructor(readonly error: Error) {}\n}\n\nexport function isReadSessionFailure(\n\tsession: ProviderAuthenticationSession | ReadSessionFailure | undefined,\n): session is ReadSessionFailure {\n\treturn session instanceof ReadSessionFailure;\n}\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\t/**\n\t * Bumped whenever a forced re-sync's refetch settles `_session`. A forced re-sync keeps the stored token while it\n\t * fetches the replacement, so a read that resolved that token concurrently must not publish it over whatever the\n\t * refetch settled on: a fresh token, or nothing after the cloud said the connection is gone.\n\t */\n\tprivate _sessionGeneration = 0;\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`. Never throws: `undefined` when there is no session to read with (the connection is gone,\n\t * the provider isn't connected), `{ error }` when the connection's token could not be fetched.\n\t *\n\t * The two must not be conflated. A token fetch that THROWS is, by `getConnectionSession`'s contract, a\n\t * retryable failure (a rate-limited or failing GK API, a network error), not evidence that the connection is\n\t * gone (#5569). Answering \"no session\" for it made every connection read during a GK API 429 report\n\t * `no-connection`, which a consumer rightly treats as a credential to reconnect (gitkraken/kepler#3546).\n\t * Returned as the read's error instead, the read core surfaces it like any other failed request: a rate\n\t * limit as `rate-limit`, anything else as `other`.\n\t */\n\tprotected async resolveReadSession(\n\t\tconnectionId: string | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tsource?: Sources,\n\t): Promise<ProviderAuthenticationSession | ReadSessionFailure | 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// Never throws, matching the primary path (whose ensureSession/refreshSessionIfExpired swallow\n\t\t\t// 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\tif (isCancellationError(ex)) return undefined;\n\n\t\t\t\tscope?.error(ex);\n\t\t\t\treturn new ReadSessionFailure(toError(ex));\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/**\n\t * {@link resolveReadSession} for a read that already rethrows its provider's failures (a repository or account\n\t * lookup), or whose callers treat any failure as \"nothing to show\": a token that could not be fetched is\n\t * thrown like those failures, so it is classified as the failed request it is rather than as a missing\n\t * connection.\n\t */\n\tprotected async resolveReadSessionOrThrow(\n\t\tconnectionId: string | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t): Promise<ProviderAuthenticationSession | undefined> {\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (isReadSessionFailure(session)) throw session.error;\n\n\t\treturn session;\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// Get a new session from the cloud. The stored one is refetched rather than deleted first (see\n\t\t\t\t\t// `GetSessionOptions.refetch`), so a fetch that fails transiently keeps the token it had.\n\t\t\t\t\t// Not left to the token-changed check below, which needs an `oldSession` — see\n\t\t\t\t\t// `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, refetch: resyncing });\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 kept the stored token and its descriptor until a replacement arrived.\n\t\t\t\t// Drop both 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\trefetch?: 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\trefetch?: 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, refetch } = options;\n\t\t// A forced re-sync keeps the stored token while it fetches the replacement, so a concurrent read can put that\n\t\t// token back in `_session` before this call runs; serving it would skip the refetch the caller forced.\n\t\tif (this._session != null && !refetch) {\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\tconst generation = this._sessionGeneration;\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, refetch: refetch, 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 (refetch) {\n\t\t\tthis._sessionGeneration++;\n\t\t} else if (generation !== this._sessionGeneration) {\n\t\t\treturn this._session ?? undefined;\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 || isReadSessionFailure(session)) return session && { error: session.error };\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 || isReadSessionFailure(session)) return session && { error: session.error };\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 || isReadSessionFailure(session)) return session && { error: session.error };\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.resolveReadSessionOrThrow(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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"}