@gitkraken/core-gitlens 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -1
- package/README.md +23 -15
- package/dist/git/errors.d.ts +6 -5
- package/dist/git/errors.js +7 -6
- package/dist/git/errors.js.map +1 -1
- package/dist/git/features.d.ts +1 -1
- package/dist/git/features.js +1 -1
- package/dist/git/features.js.map +1 -1
- package/dist/git/models/remoteProvider.d.ts +4 -1
- package/dist/git/models/remoteProvider.js.map +1 -1
- package/dist/git/providers/operations.d.ts +15 -0
- package/dist/git/providers/provider.d.ts +4 -2
- package/dist/git/utils/branch.utils.d.ts +4 -1
- package/dist/git/utils/branch.utils.js +5 -2
- package/dist/git/utils/branch.utils.js.map +1 -1
- package/dist/git/utils/mergeConflicts.utils.js +2 -2
- package/dist/git/utils/mergeConflicts.utils.js.map +1 -1
- package/dist/git/watching/worktreeSummary.d.ts +15 -3
- package/dist/git/watching/worktreeSummary.js +179 -31
- package/dist/git/watching/worktreeSummary.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +30 -3
- package/dist/git-cli/cliGitProvider.js +102 -40
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +9 -1
- package/dist/git-cli/exec/git.js +23 -4
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/gitDirResolver.d.ts +26 -0
- package/dist/git-cli/exec/gitDirResolver.js +181 -0
- package/dist/git-cli/exec/gitDirResolver.js.map +1 -0
- package/dist/git-cli/exec/headState.js +3 -3
- package/dist/git-cli/exec/headState.js.map +1 -1
- package/dist/git-cli/exec/indexRefresh.d.ts +78 -0
- package/dist/git-cli/exec/indexRefresh.js +159 -0
- package/dist/git-cli/exec/indexRefresh.js.map +1 -0
- package/dist/git-cli/providers/branchRemote.js +2 -5
- package/dist/git-cli/providers/branchRemote.js.map +1 -1
- package/dist/git-cli/providers/branches.js +4 -4
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts +2 -0
- package/dist/git-cli/providers/config.js +26 -4
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/maintenance.js +1 -1
- package/dist/git-cli/providers/operations.d.ts +4 -1
- package/dist/git-cli/providers/operations.js +33 -25
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.d.ts +3 -1
- package/dist/git-cli/providers/pausedOperations.js +10 -7
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.js +25 -1
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/staging.js +1 -1
- package/dist/git-cli/providers/stash.d.ts +3 -1
- package/dist/git-cli/providers/stash.js +8 -5
- package/dist/git-cli/providers/stash.js.map +1 -1
- package/dist/git-cli/providers/worktrees.d.ts +6 -1
- package/dist/git-cli/providers/worktrees.js +6 -2
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/git-cli/service.d.ts +2 -0
- package/dist/git-cli/service.js +2 -1
- package/dist/git-cli/service.js.map +1 -1
- package/dist/ipc/discovery.js +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/ai/errors.d.ts +4 -1
- package/dist/plus/ai/errors.js +4 -0
- package/dist/plus/ai/errors.js.map +1 -1
- package/dist/plus/ai/providers/mistralProvider.js +1 -1
- package/dist/plus/ai/providers/mistralProvider.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +2 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
- package/dist/plus/git-github/api/github.js +12 -13
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/api/token.d.ts +2 -0
- package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts +1 -1
- package/dist/plus/integrations/authentication/cloudIntegrationService.js +6 -1
- package/dist/plus/integrations/authentication/cloudIntegrationService.js.map +1 -1
- package/dist/plus/integrations/authentication/configuredIntegrationService.js +2 -2
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +18 -5
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js.map +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts +3 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationService.js +4 -0
- package/dist/plus/integrations/authentication/integrationAuthenticationService.js.map +1 -1
- package/dist/plus/integrations/authentication/models.d.ts +2 -0
- package/dist/plus/integrations/authentication/models.js +2 -1
- package/dist/plus/integrations/authentication/models.js.map +1 -1
- package/dist/plus/integrations/authentication/observedAuthenticationProvider.js +1 -1
- package/dist/plus/integrations/authentication/rejectedTokenTracker.d.ts +5 -4
- package/dist/plus/integrations/authentication/rejectedTokenTracker.js +5 -4
- package/dist/plus/integrations/authentication/rejectedTokenTracker.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +2 -2
- package/dist/plus/integrations/index.js +1 -1
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +6 -0
- package/dist/plus/integrations/integrationService.js +3 -3
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/lite.js +1 -1
- package/dist/plus/integrations/manager.d.ts +51 -3
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +14 -0
- package/dist/plus/integrations/models/gitHostIntegration.js +50 -41
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +27 -5
- package/dist/plus/integrations/models/integration.js +57 -15
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +6 -0
- package/dist/plus/integrations/models/issuesIntegration.js +11 -11
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.js +2 -2
- package/dist/plus/integrations/providers/azureDevOps.js +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.js +1 -1
- package/dist/plus/integrations/providers/bitbucket/bitbucket.js +1 -1
- package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server.js +1 -1
- package/dist/plus/integrations/providers/bitbucket.js +1 -1
- package/dist/plus/integrations/providers/github.js +1 -1
- package/dist/plus/integrations/providers/gitlab/gitlab.js +2 -2
- package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.js +1 -1
- package/dist/plus/integrations/providers/jira-server.js +2 -2
- package/dist/plus/integrations/providers/jira.js +1 -1
- package/dist/plus/integrations/providers/linear.js +2 -2
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/models.js +2 -1
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providerErrors.js +9 -0
- package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.js +1 -1
- package/dist/plus/integrations/providers/trello.js +1 -1
- package/dist/plus/integrations/providers/utils.js +1 -1
- package/dist/plus/integrations/reads/counts.js +1 -1
- package/dist/plus/integrations/reads/cursors.js +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +5 -4
- package/dist/plus/integrations/reads/drains.js +28 -8
- package/dist/plus/integrations/reads/drains.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +1 -1
- package/dist/plus/integrations/reads/issueBatch.d.ts +1 -1
- package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +1 -1
- package/dist/plus/integrations/reads/issues.d.ts +2 -0
- package/dist/plus/integrations/reads/issues.js +5 -3
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/paging.d.ts +5 -6
- package/dist/plus/integrations/reads/paging.js +5 -6
- package/dist/plus/integrations/reads/paging.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequests.d.ts +4 -0
- package/dist/plus/integrations/reads/pullRequests.js +4 -2
- package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/resolveRepository.js +1 -1
- package/dist/plus/integrations/reads/sweeps.js +42 -10
- package/dist/plus/integrations/reads/sweeps.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +23 -3
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/plus/notifications/api.d.ts +20 -0
- package/dist/plus/notifications/api.js +89 -0
- package/dist/plus/notifications/api.js.map +1 -0
- package/dist/plus/notifications/context.d.ts +28 -0
- package/dist/plus/notifications/context.js +0 -0
- package/dist/plus/notifications/errors.d.ts +13 -0
- package/dist/plus/notifications/errors.js +22 -0
- package/dist/plus/notifications/errors.js.map +1 -0
- package/dist/plus/notifications/models.d.ts +94 -0
- package/dist/plus/notifications/models.js +0 -0
- package/dist/plus/notifications/notificationsService.d.ts +115 -0
- package/dist/plus/notifications/notificationsService.js +381 -0
- package/dist/plus/notifications/notificationsService.js.map +1 -0
- package/dist/plus/notifications/parse.d.ts +12 -0
- package/dist/plus/notifications/parse.js +152 -0
- package/dist/plus/notifications/parse.js.map +1 -0
- package/dist/plus/notifications/presentation.d.ts +28 -0
- package/dist/plus/notifications/presentation.js +55 -0
- package/dist/plus/notifications/presentation.js.map +1 -0
- package/dist/utils/env/node/exec.d.ts +10 -1
- package/dist/utils/env/node/exec.js +41 -4
- package/dist/utils/env/node/exec.js.map +1 -1
- package/docs/integrations.md +21 -2
- package/docs/notifications.md +239 -0
- package/package.json +21 -1
|
@@ -173,7 +173,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
173
173
|
try {
|
|
174
174
|
const slots = await mapSettledBounded(targets, 6, (t) => getProviderIssue.call(this, session, t.resourceId, t.identifier, t.resourceUrl));
|
|
175
175
|
const settled = await this.settleBatchRefusals(session, targets, slots);
|
|
176
|
-
this.resetRequestExceptionCount("getIssue");
|
|
176
|
+
this.resetRequestExceptionCount("getIssue", connectionId);
|
|
177
177
|
return {
|
|
178
178
|
value: settled,
|
|
179
179
|
duration: performance.now() - start
|
|
@@ -212,7 +212,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
212
212
|
duration: performance.now() - start
|
|
213
213
|
};
|
|
214
214
|
const settled = await this.settleBatchRefusals(session, targets, slots, { keepOtherFailures: true });
|
|
215
|
-
if (slots.some((slot) => slot.status === "fulfilled")) this.resetRequestExceptionCount("getIssuesEtagFields");
|
|
215
|
+
if (slots.some((slot) => slot.status === "fulfilled")) this.resetRequestExceptionCount("getIssuesEtagFields", connectionId);
|
|
216
216
|
return {
|
|
217
217
|
value: settled,
|
|
218
218
|
duration: performance.now() - start
|
|
@@ -243,7 +243,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
243
243
|
if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
|
|
244
244
|
try {
|
|
245
245
|
const account = await this.getProviderAccountForResource(session, resource);
|
|
246
|
-
this.resetRequestExceptionCount("getAccountForResource");
|
|
246
|
+
this.resetRequestExceptionCount("getAccountForResource", connectionId);
|
|
247
247
|
return { value: account };
|
|
248
248
|
} catch (ex) {
|
|
249
249
|
this.handleProviderException("getAccountForResource", ex, { connectionId });
|
|
@@ -264,7 +264,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
264
264
|
if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
|
|
265
265
|
try {
|
|
266
266
|
const resources = await this.getProviderResourcesForUser(session);
|
|
267
|
-
this.resetRequestExceptionCount("getResourcesForUser");
|
|
267
|
+
this.resetRequestExceptionCount("getResourcesForUser", connectionId);
|
|
268
268
|
return { value: resources };
|
|
269
269
|
} catch (ex) {
|
|
270
270
|
this.handleProviderException("getResourcesForUser", ex, { connectionId });
|
|
@@ -289,7 +289,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
289
289
|
if (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) return { value: projects };
|
|
290
290
|
await this.confirmScopedAuthFailures(session, projects.metadata);
|
|
291
291
|
if (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) return { value: projects };
|
|
292
|
-
this.resetRequestExceptionCount("getProjectsForResources");
|
|
292
|
+
this.resetRequestExceptionCount("getProjectsForResources", connectionId);
|
|
293
293
|
return { value: projects };
|
|
294
294
|
} catch (ex) {
|
|
295
295
|
if (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) return;
|
|
@@ -323,7 +323,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
323
323
|
try {
|
|
324
324
|
const resources = await this.getProviderUncachedResourcesForUser(session);
|
|
325
325
|
if (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) return;
|
|
326
|
-
this.resetRequestExceptionCount("getResourcesForUser");
|
|
326
|
+
this.resetRequestExceptionCount("getResourcesForUser", connectionId);
|
|
327
327
|
if (resources == null) return { value: {
|
|
328
328
|
resources: void 0,
|
|
329
329
|
generation,
|
|
@@ -334,7 +334,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
334
334
|
if (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) return;
|
|
335
335
|
await this.confirmScopedAuthFailures(session, projects.metadata);
|
|
336
336
|
if (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) return;
|
|
337
|
-
this.resetRequestExceptionCount("getProjectsForResources");
|
|
337
|
+
this.resetRequestExceptionCount("getProjectsForResources", connectionId);
|
|
338
338
|
return { value: {
|
|
339
339
|
resources,
|
|
340
340
|
projects,
|
|
@@ -358,7 +358,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
358
358
|
if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
|
|
359
359
|
try {
|
|
360
360
|
const account = await this.getProviderUncachedAccountForResource(session, resource);
|
|
361
|
-
this.resetRequestExceptionCount("getAccountForResource");
|
|
361
|
+
this.resetRequestExceptionCount("getAccountForResource", connectionId);
|
|
362
362
|
return { value: account };
|
|
363
363
|
} catch (ex) {
|
|
364
364
|
this.handleProviderException("getAccountForResource", ex, { connectionId });
|
|
@@ -397,7 +397,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
397
397
|
if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
|
|
398
398
|
try {
|
|
399
399
|
const issues = await this.getProviderIssuesForProject(session, project, options);
|
|
400
|
-
this.resetRequestExceptionCount("getIssuesForProject");
|
|
400
|
+
this.resetRequestExceptionCount("getIssuesForProject", connectionId);
|
|
401
401
|
return { value: issues };
|
|
402
402
|
} catch (ex) {
|
|
403
403
|
this.handleProviderException("getIssuesForProject", ex, { connectionId });
|
|
@@ -417,7 +417,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
417
417
|
try {
|
|
418
418
|
const result = await this.getProviderIssuesForProjectWithTruncation(session, project, options);
|
|
419
419
|
await this.confirmScopedAuthFailures(session, result?.metadata);
|
|
420
|
-
this.resetRequestExceptionCount("getIssuesForProject");
|
|
420
|
+
this.resetRequestExceptionCount("getIssuesForProject", connectionId);
|
|
421
421
|
return { value: result };
|
|
422
422
|
} catch (ex) {
|
|
423
423
|
this.handleProviderException("getIssuesForProject", ex, { connectionId });
|
|
@@ -452,7 +452,7 @@ var IssuesIntegration = class extends IntegrationBase {
|
|
|
452
452
|
unsearchedIndices.push(...indices);
|
|
453
453
|
return;
|
|
454
454
|
}
|
|
455
|
-
this.resetRequestExceptionCount("getIssuesForProject");
|
|
455
|
+
this.resetRequestExceptionCount("getIssuesForProject", connectionId);
|
|
456
456
|
indices.forEach((index, i) => {
|
|
457
457
|
results[index] = { value: {
|
|
458
458
|
values: values[i],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issuesIntegration.js","names":[],"sources":["../../../../../plus/integrations/src/models/issuesIntegration.ts"],"sourcesContent":["import type { Account } from '@gitlens/git/models/author.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { mapBounded, mapSettledBounded } from '@gitlens/utils/promise.js';\nimport type { ProviderAuthenticationSession, SessionPublication } from '../authentication/models.js';\nimport { failsEveryScope } from '../collectionMetadata.js';\nimport type { IntegrationIds } from '../constants.js';\nimport { providerFanOutConcurrency } from '../constants.js';\nimport { toError } from '../errors.js';\nimport type { ProviderApiCollectionResult } from '../providers/models.js';\nimport type {\n\tBatchSlot,\n\tIntegration,\n\tIntegrationResult,\n\tIntegrationType,\n\tIssueEtagFields,\n\tIssueEtagInclude,\n} from './integration.js';\nimport { IntegrationBase, isReadSessionFailure } from './integration.js';\nimport type { IssuesForProjectOptions, ProjectIssuesDrain, ProjectIssuesRequest } from './issueReads.js';\n\n/**\n * Groups requests into searches of at most `maxPerSearch` projects sharing `searchKey`. Undefined, reading\n * every project on its own, when any request has no key, i.e. cannot be searched together with others.\n */\nexport function groupProjectIssuesSearches<P>(\n\trequests: readonly ProjectIssuesRequest<P>[],\n\tsearchKey: (request: ProjectIssuesRequest<P>) => string | undefined,\n\tmaxPerSearch: number = Number.POSITIVE_INFINITY,\n): number[][] | undefined {\n\tconst searchesByKey = new Map<string, number[][]>();\n\tfor (let index = 0; index < requests.length; index++) {\n\t\tconst key = searchKey(requests[index]);\n\t\tif (key == null) return undefined;\n\n\t\tlet searches = searchesByKey.get(key);\n\t\tif (searches == null) {\n\t\t\tsearches = [];\n\t\t\tsearchesByKey.set(key, searches);\n\t\t}\n\n\t\tlet search = searches.at(-1);\n\t\tif (search == null || search.length >= maxPerSearch) {\n\t\t\tsearch = [];\n\t\t\tsearches.push(search);\n\t\t}\n\t\tsearch.push(index);\n\t}\n\treturn [...searchesByKey.values()].flat();\n}\n\n/**\n * Splits one search's issues back to its projects, in the order of `projectKeys`. Undefined when an issue belongs to\n * no requested project (or `projectKeyOf` can't tell), since the split then can't be trusted and the search's\n * projects are read one by one instead.\n */\nexport function splitProjectIssuesSearch<I>(\n\tprojectKeys: readonly string[],\n\tissues: readonly I[],\n\tprojectKeyOf: (issue: I) => string | undefined,\n): I[][] | undefined {\n\tconst indexByKey = new Map(projectKeys.map((key, index) => [key, index]));\n\tconst split = projectKeys.map((): I[] => []);\n\tfor (const issue of issues) {\n\t\tconst key = projectKeyOf(issue);\n\t\tconst index = key != null ? indexByKey.get(key) : undefined;\n\t\tif (index == null) return undefined;\n\n\t\tsplit[index].push(issue);\n\t}\n\treturn split;\n}\n\nexport function isIssuesIntegration(integration: Integration): integration is IssuesIntegration {\n\treturn integration.type === 'issues';\n}\n\nfunction sessionPublicationKey(id: string, cloud: boolean | undefined): string {\n\treturn JSON.stringify([id, cloud]);\n}\n\nexport abstract class IssuesIntegration<\n\tID extends IntegrationIds = IntegrationIds,\n\tT extends ResourceDescriptor = ResourceDescriptor,\n> extends IntegrationBase<ID> {\n\treadonly type: IntegrationType = 'issues';\n\tprivate _discoverySequence = 0;\n\tprivate _globalRetirement = 0;\n\tprivate _primaryPublication = 0;\n\tprivate readonly _actualPublications = new Map<string, number>();\n\tprivate readonly _selectorPublications = new Map<string, number>();\n\tprivate readonly _selectorSessions = new Map<string, { id: string; cloud?: boolean }>();\n\tprivate readonly _sessionPublications = new WeakMap<\n\t\tProviderAuthenticationSession,\n\t\tMap<string, { beforeGeneration: number; afterGeneration: number }>\n\t>();\n\n\toverride get discoveryGeneration(): number {\n\t\treturn this._discoverySequence;\n\t}\n\n\toverride getSessionDiscoveryGeneration(sessionId?: string, connectionId?: string, cloud?: boolean): number {\n\t\tconst actual =\n\t\t\tsessionId == null\n\t\t\t\t? 0\n\t\t\t\t: Math.max(\n\t\t\t\t\t\tthis._actualPublications.get(sessionPublicationKey(sessionId, undefined)) ?? 0,\n\t\t\t\t\t\t...(cloud == null ? [true, false] : [cloud]).map(\n\t\t\t\t\t\t\tvariant => this._actualPublications.get(sessionPublicationKey(sessionId, variant)) ?? 0,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\treturn Math.max(\n\t\t\tthis._globalRetirement,\n\t\t\tactual,\n\t\t\tconnectionId != null ? (this._selectorPublications.get(connectionId) ?? 0) : this._primaryPublication,\n\t\t);\n\t}\n\n\toverride invalidateDiscoveryCaches(): void {\n\t\tthis._globalRetirement = ++this._discoverySequence;\n\t\tthis.clearDiscoveryCaches();\n\t}\n\n\toverride invalidateDiscoveryForConnection(connectionId?: string): void {\n\t\tconst stamp = ++this._discoverySequence;\n\t\tif (connectionId != null) {\n\t\t\tthis._selectorPublications.set(connectionId, stamp);\n\t\t\tconst session = this._selectorSessions.get(connectionId);\n\t\t\tthis._actualPublications.set(\n\t\t\t\tsessionPublicationKey(session?.id ?? connectionId, session == null ? true : session.cloud),\n\t\t\t\tstamp,\n\t\t\t);\n\t\t} else {\n\t\t\tthis._primaryPublication = stamp;\n\t\t\tif (this._session != null) {\n\t\t\t\tthis._actualPublications.set(sessionPublicationKey(this._session.id, this._session.cloud), stamp);\n\t\t\t}\n\t\t}\n\t\tthis.clearDiscoveryCaches();\n\t}\n\n\tprotected clearDiscoveryCaches(): void {}\n\n\tprotected override invalidatePublishedSession(publication: SessionPublication, affectsPrimary: boolean): void {\n\t\tconst stamp = ++this._discoverySequence;\n\t\tconst cloud = publication.session != null ? publication.session.cloud : publication.cloud;\n\t\tfor (const id of [publication.session?.id, publication.previousSessionId]) {\n\t\t\tif (id != null) {\n\t\t\t\tthis._actualPublications.set(sessionPublicationKey(id, cloud), stamp);\n\t\t\t}\n\t\t}\n\t\tif (publication.connectionId != null) {\n\t\t\tthis._selectorPublications.set(publication.connectionId, stamp);\n\t\t\tif (publication.session != null) {\n\t\t\t\tthis._selectorSessions.set(publication.connectionId, {\n\t\t\t\t\tid: publication.session.id,\n\t\t\t\t\tcloud: publication.session.cloud,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (affectsPrimary) {\n\t\t\tthis._primaryPublication = stamp;\n\t\t\tthis.clearDiscoveryCaches();\n\t\t}\n\t}\n\n\toverride onSessionPublishing(publication: SessionPublication): void {\n\t\tconst session = publication.session;\n\t\tconst transitions = session != null ? (this._sessionPublications.get(session) ?? new Map()) : undefined;\n\t\tconst selectors = new Set([\n\t\t\t...(transitions?.keys() ?? []),\n\t\t\t'',\n\t\t\tpublication.connectionId ?? '',\n\t\t\tsession?.id ?? '',\n\t\t]);\n\t\tconst before = new Map(\n\t\t\tArray.from(selectors, selector => [\n\t\t\t\tselector,\n\t\t\t\tthis.getSessionDiscoveryGeneration(session?.id, selector || undefined, session?.cloud),\n\t\t\t]),\n\t\t);\n\t\tsuper.onSessionPublishing(publication);\n\t\tif (session == null || transitions == null) return;\n\n\t\tfor (const selector of selectors) {\n\t\t\tconst beforeGeneration = before.get(selector)!;\n\t\t\tconst previous = transitions.get(selector);\n\t\t\ttransitions.set(selector, {\n\t\t\t\tbeforeGeneration:\n\t\t\t\t\tprevious?.afterGeneration === beforeGeneration ? previous.beforeGeneration : beforeGeneration,\n\t\t\t\tafterGeneration: this.getSessionDiscoveryGeneration(session.id, selector || undefined, session.cloud),\n\t\t\t});\n\t\t}\n\t\tthis._sessionPublications.set(session, transitions);\n\t}\n\n\tprivate isResolvedDiscoverySessionCurrent(\n\t\tsession: ProviderAuthenticationSession,\n\t\tstartingGeneration: number,\n\t\tconnectionId: string | undefined,\n\t): boolean {\n\t\tif (!connectionId && session !== this._session) return false;\n\t\treturn this.isResolvedSessionCurrent(session, startingGeneration, connectionId);\n\t}\n\n\tprotected override isResolvedSessionCurrent(\n\t\tsession: ProviderAuthenticationSession | undefined,\n\t\tstartingGeneration: number,\n\t\tconnectionId?: string,\n\t): boolean {\n\t\tif (session == null) return startingGeneration === this.discoveryGeneration;\n\n\t\tconst current = this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud);\n\t\tif (current <= startingGeneration) return true;\n\n\t\t// Only this resolution's publications may bridge a retirement boundary.\n\t\tconst publication = this._sessionPublications.get(session)?.get(connectionId ?? '');\n\t\treturn (\n\t\t\tpublication != null &&\n\t\t\tpublication.beforeGeneration <= startingGeneration &&\n\t\t\tpublication.afterGeneration === current\n\t\t);\n\t}\n\n\tget supportsIssueLookupByResourceId(): boolean {\n\t\treturn this.getProviderIssueByResourceId != null;\n\t}\n\n\t/**\n\t * Whether this tracker has the cheap check behind the batch issue read's etags\n\t * ({@link getProviderIssuesEtagFieldsByResourceId}). The read decides before calling anything, so a tracker without\n\t * one makes one call.\n\t */\n\tget supportsIssueEtagsByResourceId(): boolean {\n\t\treturn this.getProviderIssuesEtagFieldsByResourceId != null;\n\t}\n\n\t/**\n\t * Result-returning wrapper for the BATCH tracker issue read: resolves several `(resourceId, identifier)` targets\n\t * with one {@link getProviderIssueByResourceId} request each, run with bounded concurrency. One settled slot per\n\t * input target: `fulfilled` with `undefined` means the issue does not exist or is not visible to this token;\n\t * `rejected` means that target could not be checked, never that it is absent.\n\t *\n\t * Failure isolation happens PER TARGET, mirroring `GitHostIntegration.getPullRequestsBatchResult`: the whole\n\t * call counts as a failure against the integration's request-exception budget only when EVERY slot rejected,\n\t * and not even then when a credential that checks out was refused by each target's own resource\n\t * (`settleBatchRefusals`), so a batch of mostly-good targets never spends more than one strike. A batch read with\n\t * etags makes up to two such calls (a full read of the targets without an etag, and a full read of the ones that\n\t * changed), so it can spend two; its cheap check ({@link getIssuesEtagFieldsByResourceIdBatchResult}) spends\n\t * nothing unless the credential is refused.\n\t *\n\t * Uncached on purpose: the caller owns caching, as with the git hosts' batch reads.\n\t */\n\tasync getIssuesByResourceIdBatchResult(\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<BatchSlot<IssueShape | undefined>[] | 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 getProviderIssue = this.getProviderIssueByResourceId;\n\t\tif (getProviderIssue == null) return { value: undefined };\n\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tconst slots = await mapSettledBounded(targets, providerFanOutConcurrency, t =>\n\t\t\t\tgetProviderIssue.call(this, session, t.resourceId, t.identifier, t.resourceUrl),\n\t\t\t);\n\n\t\t\tconst settled = await this.settleBatchRefusals(session, targets, slots);\n\n\t\t\tthis.resetRequestExceptionCount('getIssue');\n\t\t\treturn { value: settled, duration: performance.now() - start };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssue', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex), duration: performance.now() - start };\n\t\t}\n\t}\n\n\tprotected getProviderIssueByResourceId?(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_resourceId: string,\n\t\t_id: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined>;\n\n\t/**\n\t * Result-returning wrapper for the cheap check behind the batch tracker issue read's etags: each target's change\n\t * state ({@link IssueEtagFields}), in ONE call to {@link getProviderIssuesEtagFieldsByResourceId}, which is handed\n\t * `options.etagIncludes` as the git hosts' check is. The slots mean\n\t * what {@link getIssuesByResourceIdBatchResult}'s do — `fulfilled` with `undefined` is a PROVEN ABSENCE, `rejected`\n\t * means that target could not be checked. Failures are judged and budgeted as in\n\t * `GitHostIntegration.getIssuesEtagFieldsResult`: every slot comes back as it settled, even when none answered, and\n\t * only a refused credential fails the call or spends a strike.\n\t */\n\tasync getIssuesEtagFieldsByResourceIdBatchResult(\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t\toptions: { etagIncludes?: readonly IssueEtagInclude[] },\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<BatchSlot<IssueEtagFields | undefined>[] | 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 getProviderEtagFields = this.getProviderIssuesEtagFieldsByResourceId;\n\t\tif (getProviderEtagFields == null) return { value: undefined };\n\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tconst slots = await getProviderEtagFields.call(this, session, targets, options);\n\t\t\tif (slots == null) return { value: undefined, duration: performance.now() - start };\n\n\t\t\tconst settled = await this.settleBatchRefusals(session, targets, slots, { keepOtherFailures: true });\n\n\t\t\tif (slots.some(slot => slot.status === 'fulfilled')) {\n\t\t\t\tthis.resetRequestExceptionCount('getIssuesEtagFields');\n\t\t\t}\n\t\t\treturn { value: settled, duration: performance.now() - start };\n\t\t} catch (ex) {\n\t\t\tthis.handleEtagCheckException('getIssuesEtagFields', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex), duration: performance.now() - start };\n\t\t}\n\t}\n\n\t/**\n\t * OPTIONAL: the cheap check behind the batch tracker issue read's etags — one settled slot per input target, in\n\t * order, holding the issue's change state in the vocabulary {@link getProviderIssueByResourceId}'s issues end in,\n\t * so both reads compute the same etag. `fulfilled` with `undefined` is a PROVEN ABSENCE, as there; a tracker whose\n\t * cheap read can't prove one rejects the slot instead, so the full read decides. `undefined` declines the whole\n\t * check, sending every target to the full read without counting as a failure. A tracker implements this only\n\t * where it is cheaper than the full read. No tracker's row carries a real reaction count, so `'reactions'` in\n\t * `options.etagIncludes` reads nothing here.\n\t */\n\tprotected getProviderIssuesEtagFieldsByResourceId?(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t\toptions: { etagIncludes?: readonly IssueEtagInclude[] },\n\t): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined>;\n\n\t@trace()\n\tasync getAccountForResource(resource: T, connectionId?: string): Promise<Account | undefined> {\n\t\treturn (await this.getAccountForResourceResult(resource, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getAccountForResource}. Recovers a thrown error into `{ error }` so\n\t * callers can preserve its classification (e.g. a 401/403 → an `auth` warning that drives re-auth)\n\t * instead of collapsing every failure into an untyped `undefined`. Gated here (not on the wrapper) so\n\t * direct callers such as the ProviderBackend facade share the same dedup as `getAccountForResource`.\n\t */\n\t@gate()\n\tasync getAccountForResourceResult(\n\t\tresource: T,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<Account | 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\ttry {\n\t\t\tconst account = await this.getProviderAccountForResource(session, resource);\n\t\t\tthis.resetRequestExceptionCount('getAccountForResource');\n\t\t\treturn { value: account };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getAccountForResource', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t): Promise<Account | undefined>;\n\n\t@trace()\n\tasync getResourcesForUser(connectionId?: string): Promise<T[] | undefined> {\n\t\treturn (await this.getResourcesForUserResult(connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getResourcesForUser}. Recovers thrown errors into `{ error }` so callers\n\t * can surface them as warnings rather than silently swallowing them to `undefined`. Gated here (not on the\n\t * wrapper) so direct callers such as the ProviderBackend facade share the same dedup as `getResourcesForUser`.\n\t */\n\t@gate()\n\tasync getResourcesForUserResult(connectionId?: string): Promise<IntegrationResult<T[] | 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\ttry {\n\t\t\tconst resources = await this.getProviderResourcesForUser(session);\n\t\t\tthis.resetRequestExceptionCount('getResourcesForUser');\n\t\t\treturn { value: resources };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getResourcesForUser', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderResourcesForUser(session: ProviderAuthenticationSession): Promise<T[] | undefined>;\n\n\t/**\n\t * Project discovery for a set of resources, returning the SDK collection `{ values, metadata }` (completeness\n\t * + per-resource failures) so callers can warn on failed resources and set `fetchFailed` without discarding\n\t * the resources that succeeded. Thrown errors are recovered into `{ error }` for the same reason: a caller\n\t * surfaces them as a warning rather than swallowing them to `undefined`.\n\t */\n\t@trace()\n\tasync getProjectsForResourcesWithMetadataResult(\n\t\tresources: T[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProviderApiCollectionResult<T> | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\tconst startingGeneration = this.discoveryGeneration;\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\t\t// Primary resolution can refresh its own session before discovery starts.\n\t\tif (!this.isResolvedDiscoverySessionCurrent(session, startingGeneration, connectionId)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst generation = this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud);\n\n\t\ttry {\n\t\t\tconst projects = await this.getProviderProjectsForResourcesWithMetadata(session, resources);\n\t\t\t// Existing cached readers keep their snapshot after invalidation without changing the replacement session.\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn { value: projects };\n\t\t\t}\n\n\t\t\tawait this.confirmScopedAuthFailures(session, projects.metadata);\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn { value: projects };\n\t\t\t}\n\n\t\t\tthis.resetRequestExceptionCount('getProjectsForResources');\n\t\t\treturn { value: projects };\n\t\t} catch (ex) {\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.handleProviderException('getProjectsForResources', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Metadata-aware provider project discovery. The default wraps the array-returning\n\t * {@link getProviderProjectsForResources} in `{ values }`, so providers without a fan-out completeness signal\n\t * (Jira Data Center, Trello) need no change; a read that returned no data is `unknown`, not an empty project list.\n\t * Jira Cloud and Linear override it to preserve their completeness/failures.\n\t */\n\tprotected async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<ProviderApiCollectionResult<T>> {\n\t\treturn toProjectsCollection(await this.getProviderProjectsForResources(session, resources));\n\t}\n\n\tprotected abstract getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<T[] | undefined>;\n\n\t/**\n\t * The resources, and the projects of those `projectsOf` keeps, as the provider lists them now. For a read that must\n\t * not take a cached list for the current one: what it decides from this snapshot (that a selection is gone) would\n\t * be wrong for as long as the cache is stale. Shares nothing with the cached discovery: joins no read in flight,\n\t * reads and writes no cache, so it can neither be answered by a read started before what it needs to see, nor put\n\t * its answer into a cache a concurrent read relies on. `projects` is omitted when no resource list came back.\n\t */\n\t@trace()\n\tasync getUncachedDiscoveryResult(\n\t\tprojectsOf: (resource: T) => boolean,\n\t\tconnectionId?: string,\n\t): Promise<\n\t\tIntegrationResult<\n\t\t\t| {\n\t\t\t\t\tresources: T[] | undefined;\n\t\t\t\t\tprojects?: ProviderApiCollectionResult<T>;\n\t\t\t\t\tgeneration: number;\n\t\t\t\t\tsessionId: string;\n\t\t\t\t\tsessionCloud?: boolean;\n\t\t\t }\n\t\t\t| undefined\n\t\t>\n\t> {\n\t\tconst scope = getScopedLogger();\n\t\tconst startingGeneration = this.discoveryGeneration;\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null || isReadSessionFailure(session)) return session && { error: session.error };\n\t\tif (!this.isResolvedDiscoverySessionCurrent(session, startingGeneration, connectionId)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst generation = this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud);\n\n\t\ttry {\n\t\t\t// Reset on success as the cached reads are, so a refusal a re-sync healed doesn't count toward a disconnect.\n\t\t\tconst resources = await this.getProviderUncachedResourcesForUser(session);\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.resetRequestExceptionCount('getResourcesForUser');\n\t\t\tif (resources == null) {\n\t\t\t\treturn {\n\t\t\t\t\tvalue: {\n\t\t\t\t\t\tresources: undefined,\n\t\t\t\t\t\tgeneration: generation,\n\t\t\t\t\t\tsessionId: session.id,\n\t\t\t\t\t\tsessionCloud: session.cloud,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst projects = await this.getProviderUncachedProjectsForResourcesWithMetadata(\n\t\t\t\tsession,\n\t\t\t\tresources.filter(projectsOf),\n\t\t\t);\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tawait this.confirmScopedAuthFailures(session, projects.metadata);\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.resetRequestExceptionCount('getProjectsForResources');\n\t\t\treturn {\n\t\t\t\tvalue: {\n\t\t\t\t\tresources: resources,\n\t\t\t\t\tprojects: projects,\n\t\t\t\t\tgeneration: generation,\n\t\t\t\t\tsessionId: session.id,\n\t\t\t\t\tsessionCloud: session.cloud,\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.handleProviderException('getProjectsForResources', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * {@link getAccountForResourceResult} asked of the provider now, joining no lookup in flight and served from no\n\t * account it lends: a lookup started before the credential was revoked would vouch for it.\n\t */\n\t@trace()\n\tasync getUncachedAccountForResourceResult(\n\t\tresource: T,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<Account | 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 account = await this.getProviderUncachedAccountForResource(session, resource);\n\t\t\tthis.resetRequestExceptionCount('getAccountForResource');\n\t\t\treturn { value: account };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getAccountForResource', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/** The account, asked of the provider; the default is {@link getProviderAccountForResource}, which lends none. */\n\tprotected getProviderUncachedAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t): Promise<Account | undefined> {\n\t\treturn this.getProviderAccountForResource(session, resource);\n\t}\n\n\t/**\n\t * The provider's resources, read past any cache it keeps and without storing into it. The default is\n\t * {@link getProviderResourcesForUser}, right for a provider that keeps none.\n\t */\n\tprotected getProviderUncachedResourcesForUser(session: ProviderAuthenticationSession): Promise<T[] | undefined> {\n\t\treturn this.getProviderResourcesForUser(session);\n\t}\n\n\t/**\n\t * The projects of `resources`, read past any cache the provider keeps and without storing into it. The default is\n\t * {@link getProviderProjectsForResourcesWithMetadata}, right for a provider that keeps none.\n\t */\n\tprotected getProviderUncachedProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<ProviderApiCollectionResult<T>> {\n\t\treturn this.getProviderProjectsForResourcesWithMetadata(session, resources);\n\t}\n\n\t@trace()\n\tasync getIssuesForProject(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getIssuesForProjectResult(project, options, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getIssuesForProject}. Recovers thrown errors into `{ error }` so callers\n\t * (e.g. the ProviderBackend facade) can surface a per-provider warning instead of a silent empty read —\n\t * important for providers that throw on unsupported operations (e.g. Linear's not-implemented issue read).\n\t */\n\tasync getIssuesForProjectResult(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\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\ttry {\n\t\t\tconst issues = await this.getProviderIssuesForProject(session, project, options);\n\t\t\tthis.resetRequestExceptionCount('getIssuesForProject');\n\t\t\treturn { value: issues };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Truncation-aware variant of {@link getIssuesForProjectResult}. A provider that drains a project's issues\n\t * with an internal page backstop (Jira/Linear) overrides {@link getProviderIssuesForProjectWithTruncation}\n\t * to report when that backstop was hit, so the facade can surface an incomplete project read instead of\n\t * publishing it as complete. The default reports `truncated: false`.\n\t */\n\tasync getIssuesForProjectWithTruncationResult(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProjectIssuesDrain | 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.getProviderIssuesForProjectWithTruncation(session, project, options);\n\t\t\tawait this.confirmScopedAuthFailures(session, result?.metadata);\n\t\t\tthis.resetRequestExceptionCount('getIssuesForProject');\n\t\t\treturn { value: result };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Reads several projects' issues, one result per request in request order, each with the contract of\n\t * {@link getIssuesForProjectWithTruncationResult}, so one project failing leaves its siblings readable.\n\t *\n\t * A tracker that can read several projects in one query (Jira, Linear) groups them through\n\t * {@link getProjectIssuesSearches}, so a project with none of the user's issues costs no request of its own.\n\t * Every other request, and every project of a search that did not complete, is read on its own.\n\t *\n\t * A search's pages are global to its project set, so only a search that completes says something about each\n\t * of its projects: one that does not, or that fails for a reason that may be one project's\n\t * ({@link failsEveryScope}), is read again project by project, which reports completeness, failures and\n\t * retries per project. Only a failure every project would hit on its own read too fails them all at once.\n\t */\n\tasync getIssuesForProjectsWithTruncationResult(\n\t\trequests: readonly ProjectIssuesRequest<T>[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProjectIssuesDrain | undefined>[]> {\n\t\tconst searches = this.getProjectIssuesSearches(requests);\n\t\tif (searches == null) return this.readProjectsOneByOne(requests, connectionId);\n\n\t\tconst scope = getScopedLogger();\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null || isReadSessionFailure(session)) {\n\t\t\treturn requests.map(() => session && { error: session.error });\n\t\t}\n\n\t\tconst results: IntegrationResult<ProjectIssuesDrain | undefined>[] = requests.map(() => undefined);\n\t\tconst unsearchedIndices: number[] = [];\n\t\tawait mapBounded(searches, providerFanOutConcurrency, async indices => {\n\t\t\ttry {\n\t\t\t\tconst values = await this.searchProviderProjectIssues(\n\t\t\t\t\tsession,\n\t\t\t\t\tindices.map(index => requests[index]),\n\t\t\t\t);\n\t\t\t\tif (values == null) {\n\t\t\t\t\tunsearchedIndices.push(...indices);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.resetRequestExceptionCount('getIssuesForProject');\n\t\t\t\tindices.forEach((index, i) => {\n\t\t\t\t\tresults[index] = { value: { values: values[i], truncated: false } };\n\t\t\t\t});\n\t\t\t} catch (ex) {\n\t\t\t\tif (!failsEveryScope(ex)) {\n\t\t\t\t\tLogger.warn(scope, `Project search failed (${String(ex)}); reading its projects one by one`);\n\t\t\t\t\tunsearchedIndices.push(...indices);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\t\tconst error = toError(ex);\n\t\t\t\tfor (const index of indices) {\n\t\t\t\t\tresults[index] = { error: error };\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// After every search rather than inside each, so the fallback reads share one concurrency bound.\n\t\tif (unsearchedIndices.length > 0) {\n\t\t\tconst fallback = await this.readProjectsOneByOne(\n\t\t\t\tunsearchedIndices.map(index => requests[index]),\n\t\t\t\tconnectionId,\n\t\t\t);\n\t\t\tunsearchedIndices.forEach((index, i) => {\n\t\t\t\tresults[index] = fallback[i];\n\t\t\t});\n\t\t}\n\t\treturn results;\n\t}\n\n\t/**\n\t * Groups requests into searches, as lists of request indices, that {@link searchProviderProjectIssues} can read\n\t * in one query each. Undefined reads every project on its own, which is the default.\n\t */\n\tprotected getProjectIssuesSearches(_requests: readonly ProjectIssuesRequest<T>[]): number[][] | undefined {\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Reads the projects of one search in one query: each project's issues in request order, or undefined when the\n\t * query did not complete, which reads them one by one instead. A throw is classified by {@link failsEveryScope}.\n\t */\n\tprotected searchProviderProjectIssues(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_requests: readonly ProjectIssuesRequest<T>[],\n\t): Promise<IssueShape[][] | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\tprivate readProjectsOneByOne(\n\t\trequests: readonly ProjectIssuesRequest<T>[],\n\t\tconnectionId: string | undefined,\n\t): Promise<IntegrationResult<ProjectIssuesDrain | undefined>[]> {\n\t\treturn mapBounded(requests, providerFanOutConcurrency, async request => {\n\t\t\ttry {\n\t\t\t\treturn await this.getIssuesForProjectWithTruncationResult(\n\t\t\t\t\trequest.project,\n\t\t\t\t\trequest.options,\n\t\t\t\t\tconnectionId,\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\treturn { error: toError(ex) };\n\t\t\t}\n\t\t});\n\t}\n\n\tprotected abstract getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined>;\n\n\t/**\n\t * Truncation-aware core of {@link getProviderIssuesForProject}. The default wraps the plain read and\n\t * reports `truncated: false`; a provider whose per-project drain is capped by a page backstop overrides\n\t * this to report incompleteness. Optional metadata lets providers surface structured per-project failures\n\t * (e.g. a page-level auth rejection) without discarding the already-fetched prefix.\n\t */\n\tprotected async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tconst values = await this.getProviderIssuesForProject(session, project, options);\n\t\tif (values == null) return undefined;\n\t\treturn { values: values, truncated: false };\n\t}\n}\n\n/** A project list as a collection: no data is no list, `unknown`, never an empty complete one. */\nexport function toProjectsCollection<T extends ResourceDescriptor>(\n\tprojects: T[] | undefined,\n): ProviderApiCollectionResult<T> {\n\treturn projects != null ? { values: projects } : { values: [], metadata: { completeness: 'unknown' } };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"issuesIntegration.js","names":[],"sources":["../../../../../plus/integrations/src/models/issuesIntegration.ts"],"sourcesContent":["import type { Account } from '@gitlens/git/models/author.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { mapBounded, mapSettledBounded } from '@gitlens/utils/promise.js';\nimport type { ProviderAuthenticationSession, SessionPublication } from '../authentication/models.js';\nimport { failsEveryScope } from '../collectionMetadata.js';\nimport type { IntegrationIds } from '../constants.js';\nimport { providerFanOutConcurrency } from '../constants.js';\nimport { toError } from '../errors.js';\nimport type { ProviderApiCollectionResult } from '../providers/models.js';\nimport type {\n\tBatchSlot,\n\tIntegration,\n\tIntegrationResult,\n\tIntegrationType,\n\tIssueEtagFields,\n\tIssueEtagInclude,\n} from './integration.js';\nimport { IntegrationBase, isReadSessionFailure } from './integration.js';\nimport type { IssuesForProjectOptions, ProjectIssuesDrain, ProjectIssuesRequest } from './issueReads.js';\n\n/**\n * Groups requests into searches of at most `maxPerSearch` projects sharing `searchKey`. Undefined, reading\n * every project on its own, when any request has no key, i.e. cannot be searched together with others.\n */\nexport function groupProjectIssuesSearches<P>(\n\trequests: readonly ProjectIssuesRequest<P>[],\n\tsearchKey: (request: ProjectIssuesRequest<P>) => string | undefined,\n\tmaxPerSearch: number = Number.POSITIVE_INFINITY,\n): number[][] | undefined {\n\tconst searchesByKey = new Map<string, number[][]>();\n\tfor (let index = 0; index < requests.length; index++) {\n\t\tconst key = searchKey(requests[index]);\n\t\tif (key == null) return undefined;\n\n\t\tlet searches = searchesByKey.get(key);\n\t\tif (searches == null) {\n\t\t\tsearches = [];\n\t\t\tsearchesByKey.set(key, searches);\n\t\t}\n\n\t\tlet search = searches.at(-1);\n\t\tif (search == null || search.length >= maxPerSearch) {\n\t\t\tsearch = [];\n\t\t\tsearches.push(search);\n\t\t}\n\t\tsearch.push(index);\n\t}\n\treturn [...searchesByKey.values()].flat();\n}\n\n/**\n * Splits one search's issues back to its projects, in the order of `projectKeys`. Undefined when an issue belongs to\n * no requested project (or `projectKeyOf` can't tell), since the split then can't be trusted and the search's\n * projects are read one by one instead.\n */\nexport function splitProjectIssuesSearch<I>(\n\tprojectKeys: readonly string[],\n\tissues: readonly I[],\n\tprojectKeyOf: (issue: I) => string | undefined,\n): I[][] | undefined {\n\tconst indexByKey = new Map(projectKeys.map((key, index) => [key, index]));\n\tconst split = projectKeys.map((): I[] => []);\n\tfor (const issue of issues) {\n\t\tconst key = projectKeyOf(issue);\n\t\tconst index = key != null ? indexByKey.get(key) : undefined;\n\t\tif (index == null) return undefined;\n\n\t\tsplit[index].push(issue);\n\t}\n\treturn split;\n}\n\nexport function isIssuesIntegration(integration: Integration): integration is IssuesIntegration {\n\treturn integration.type === 'issues';\n}\n\nfunction sessionPublicationKey(id: string, cloud: boolean | undefined): string {\n\treturn JSON.stringify([id, cloud]);\n}\n\nexport abstract class IssuesIntegration<\n\tID extends IntegrationIds = IntegrationIds,\n\tT extends ResourceDescriptor = ResourceDescriptor,\n> extends IntegrationBase<ID> {\n\treadonly type: IntegrationType = 'issues';\n\tprivate _discoverySequence = 0;\n\tprivate _globalRetirement = 0;\n\tprivate _primaryPublication = 0;\n\tprivate readonly _actualPublications = new Map<string, number>();\n\tprivate readonly _selectorPublications = new Map<string, number>();\n\tprivate readonly _selectorSessions = new Map<string, { id: string; cloud?: boolean }>();\n\tprivate readonly _sessionPublications = new WeakMap<\n\t\tProviderAuthenticationSession,\n\t\tMap<string, { beforeGeneration: number; afterGeneration: number }>\n\t>();\n\n\toverride get discoveryGeneration(): number {\n\t\treturn this._discoverySequence;\n\t}\n\n\toverride getSessionDiscoveryGeneration(sessionId?: string, connectionId?: string, cloud?: boolean): number {\n\t\tconst actual =\n\t\t\tsessionId == null\n\t\t\t\t? 0\n\t\t\t\t: Math.max(\n\t\t\t\t\t\tthis._actualPublications.get(sessionPublicationKey(sessionId, undefined)) ?? 0,\n\t\t\t\t\t\t...(cloud == null ? [true, false] : [cloud]).map(\n\t\t\t\t\t\t\tvariant => this._actualPublications.get(sessionPublicationKey(sessionId, variant)) ?? 0,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\treturn Math.max(\n\t\t\tthis._globalRetirement,\n\t\t\tactual,\n\t\t\tconnectionId != null ? (this._selectorPublications.get(connectionId) ?? 0) : this._primaryPublication,\n\t\t);\n\t}\n\n\toverride invalidateDiscoveryCaches(): void {\n\t\tthis._globalRetirement = ++this._discoverySequence;\n\t\tthis.clearDiscoveryCaches();\n\t}\n\n\toverride invalidateDiscoveryForConnection(connectionId?: string): void {\n\t\tconst stamp = ++this._discoverySequence;\n\t\tif (connectionId != null) {\n\t\t\tthis._selectorPublications.set(connectionId, stamp);\n\t\t\tconst session = this._selectorSessions.get(connectionId);\n\t\t\tthis._actualPublications.set(\n\t\t\t\tsessionPublicationKey(session?.id ?? connectionId, session == null ? true : session.cloud),\n\t\t\t\tstamp,\n\t\t\t);\n\t\t} else {\n\t\t\tthis._primaryPublication = stamp;\n\t\t\tif (this._session != null) {\n\t\t\t\tthis._actualPublications.set(sessionPublicationKey(this._session.id, this._session.cloud), stamp);\n\t\t\t}\n\t\t}\n\t\tthis.clearDiscoveryCaches();\n\t}\n\n\tprotected clearDiscoveryCaches(): void {}\n\n\tprotected override invalidatePublishedSession(publication: SessionPublication, affectsPrimary: boolean): void {\n\t\tconst stamp = ++this._discoverySequence;\n\t\tconst cloud = publication.session != null ? publication.session.cloud : publication.cloud;\n\t\tfor (const id of [publication.session?.id, publication.previousSessionId]) {\n\t\t\tif (id != null) {\n\t\t\t\tthis._actualPublications.set(sessionPublicationKey(id, cloud), stamp);\n\t\t\t}\n\t\t}\n\t\tif (publication.connectionId != null) {\n\t\t\tthis._selectorPublications.set(publication.connectionId, stamp);\n\t\t\tif (publication.session != null) {\n\t\t\t\tthis._selectorSessions.set(publication.connectionId, {\n\t\t\t\t\tid: publication.session.id,\n\t\t\t\t\tcloud: publication.session.cloud,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (affectsPrimary) {\n\t\t\tthis._primaryPublication = stamp;\n\t\t\tthis.clearDiscoveryCaches();\n\t\t}\n\t}\n\n\toverride onSessionPublishing(publication: SessionPublication): void {\n\t\tconst session = publication.session;\n\t\tconst transitions = session != null ? (this._sessionPublications.get(session) ?? new Map()) : undefined;\n\t\tconst selectors = new Set([\n\t\t\t...(transitions?.keys() ?? []),\n\t\t\t'',\n\t\t\tpublication.connectionId ?? '',\n\t\t\tsession?.id ?? '',\n\t\t]);\n\t\tconst before = new Map(\n\t\t\tArray.from(selectors, selector => [\n\t\t\t\tselector,\n\t\t\t\tthis.getSessionDiscoveryGeneration(session?.id, selector || undefined, session?.cloud),\n\t\t\t]),\n\t\t);\n\t\tsuper.onSessionPublishing(publication);\n\t\tif (session == null || transitions == null) return;\n\n\t\tfor (const selector of selectors) {\n\t\t\tconst beforeGeneration = before.get(selector)!;\n\t\t\tconst previous = transitions.get(selector);\n\t\t\ttransitions.set(selector, {\n\t\t\t\tbeforeGeneration:\n\t\t\t\t\tprevious?.afterGeneration === beforeGeneration ? previous.beforeGeneration : beforeGeneration,\n\t\t\t\tafterGeneration: this.getSessionDiscoveryGeneration(session.id, selector || undefined, session.cloud),\n\t\t\t});\n\t\t}\n\t\tthis._sessionPublications.set(session, transitions);\n\t}\n\n\tprivate isResolvedDiscoverySessionCurrent(\n\t\tsession: ProviderAuthenticationSession,\n\t\tstartingGeneration: number,\n\t\tconnectionId: string | undefined,\n\t): boolean {\n\t\tif (!connectionId && session !== this._session) return false;\n\t\treturn this.isResolvedSessionCurrent(session, startingGeneration, connectionId);\n\t}\n\n\tprotected override isResolvedSessionCurrent(\n\t\tsession: ProviderAuthenticationSession | undefined,\n\t\tstartingGeneration: number,\n\t\tconnectionId?: string,\n\t): boolean {\n\t\tif (session == null) return startingGeneration === this.discoveryGeneration;\n\n\t\tconst current = this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud);\n\t\tif (current <= startingGeneration) return true;\n\n\t\t// Only this resolution's publications may bridge a retirement boundary.\n\t\tconst publication = this._sessionPublications.get(session)?.get(connectionId ?? '');\n\t\treturn (\n\t\t\tpublication != null &&\n\t\t\tpublication.beforeGeneration <= startingGeneration &&\n\t\t\tpublication.afterGeneration === current\n\t\t);\n\t}\n\n\tget supportsIssueLookupByResourceId(): boolean {\n\t\treturn this.getProviderIssueByResourceId != null;\n\t}\n\n\t/**\n\t * Whether this tracker has the cheap check behind the batch issue read's etags\n\t * ({@link getProviderIssuesEtagFieldsByResourceId}). The read decides before calling anything, so a tracker without\n\t * one makes one call.\n\t */\n\tget supportsIssueEtagsByResourceId(): boolean {\n\t\treturn this.getProviderIssuesEtagFieldsByResourceId != null;\n\t}\n\n\t/**\n\t * Result-returning wrapper for the BATCH tracker issue read: resolves several `(resourceId, identifier)` targets\n\t * with one {@link getProviderIssueByResourceId} request each, run with bounded concurrency. One settled slot per\n\t * input target: `fulfilled` with `undefined` means the issue does not exist or is not visible to this token;\n\t * `rejected` means that target could not be checked, never that it is absent.\n\t *\n\t * Failure isolation happens PER TARGET, mirroring `GitHostIntegration.getPullRequestsBatchResult`: the whole\n\t * call counts as a failure against the integration's request-exception budget only when EVERY slot rejected,\n\t * and not even then when a credential that checks out was refused by each target's own resource\n\t * (`settleBatchRefusals`), so a batch of mostly-good targets never spends more than one strike. A batch read with\n\t * etags makes up to two such calls (a full read of the targets without an etag, and a full read of the ones that\n\t * changed), so it can spend two; its cheap check ({@link getIssuesEtagFieldsByResourceIdBatchResult}) spends\n\t * nothing unless the credential is refused.\n\t *\n\t * Uncached on purpose: the caller owns caching, as with the git hosts' batch reads.\n\t */\n\tasync getIssuesByResourceIdBatchResult(\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<BatchSlot<IssueShape | undefined>[] | 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 getProviderIssue = this.getProviderIssueByResourceId;\n\t\tif (getProviderIssue == null) return { value: undefined };\n\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tconst slots = await mapSettledBounded(targets, providerFanOutConcurrency, t =>\n\t\t\t\tgetProviderIssue.call(this, session, t.resourceId, t.identifier, t.resourceUrl),\n\t\t\t);\n\n\t\t\tconst settled = await this.settleBatchRefusals(session, targets, slots);\n\n\t\t\tthis.resetRequestExceptionCount('getIssue', connectionId);\n\t\t\treturn { value: settled, duration: performance.now() - start };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssue', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex), duration: performance.now() - start };\n\t\t}\n\t}\n\n\tprotected getProviderIssueByResourceId?(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_resourceId: string,\n\t\t_id: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined>;\n\n\t/**\n\t * Result-returning wrapper for the cheap check behind the batch tracker issue read's etags: each target's change\n\t * state ({@link IssueEtagFields}), in ONE call to {@link getProviderIssuesEtagFieldsByResourceId}, which is handed\n\t * `options.etagIncludes` as the git hosts' check is. The slots mean\n\t * what {@link getIssuesByResourceIdBatchResult}'s do — `fulfilled` with `undefined` is a PROVEN ABSENCE, `rejected`\n\t * means that target could not be checked. Failures are judged and budgeted as in\n\t * `GitHostIntegration.getIssuesEtagFieldsResult`: every slot comes back as it settled, even when none answered, and\n\t * only a refused credential fails the call or spends a strike.\n\t */\n\tasync getIssuesEtagFieldsByResourceIdBatchResult(\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t\toptions: { etagIncludes?: readonly IssueEtagInclude[] },\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<BatchSlot<IssueEtagFields | undefined>[] | 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 getProviderEtagFields = this.getProviderIssuesEtagFieldsByResourceId;\n\t\tif (getProviderEtagFields == null) return { value: undefined };\n\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tconst slots = await getProviderEtagFields.call(this, session, targets, options);\n\t\t\tif (slots == null) return { value: undefined, duration: performance.now() - start };\n\n\t\t\tconst settled = await this.settleBatchRefusals(session, targets, slots, { keepOtherFailures: true });\n\n\t\t\tif (slots.some(slot => slot.status === 'fulfilled')) {\n\t\t\t\tthis.resetRequestExceptionCount('getIssuesEtagFields', connectionId);\n\t\t\t}\n\t\t\treturn { value: settled, duration: performance.now() - start };\n\t\t} catch (ex) {\n\t\t\tthis.handleEtagCheckException('getIssuesEtagFields', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex), duration: performance.now() - start };\n\t\t}\n\t}\n\n\t/**\n\t * OPTIONAL: the cheap check behind the batch tracker issue read's etags — one settled slot per input target, in\n\t * order, holding the issue's change state in the vocabulary {@link getProviderIssueByResourceId}'s issues end in,\n\t * so both reads compute the same etag. `fulfilled` with `undefined` is a PROVEN ABSENCE, as there; a tracker whose\n\t * cheap read can't prove one rejects the slot instead, so the full read decides. `undefined` declines the whole\n\t * check, sending every target to the full read without counting as a failure. A tracker implements this only\n\t * where it is cheaper than the full read. No tracker's row carries a real reaction count, so `'reactions'` in\n\t * `options.etagIncludes` reads nothing here.\n\t */\n\tprotected getProviderIssuesEtagFieldsByResourceId?(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t\toptions: { etagIncludes?: readonly IssueEtagInclude[] },\n\t): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined>;\n\n\t@trace()\n\tasync getAccountForResource(resource: T, connectionId?: string): Promise<Account | undefined> {\n\t\treturn (await this.getAccountForResourceResult(resource, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getAccountForResource}. Recovers a thrown error into `{ error }` so\n\t * callers can preserve its classification (e.g. a 401/403 → an `auth` warning that drives re-auth)\n\t * instead of collapsing every failure into an untyped `undefined`. Gated here (not on the wrapper) so\n\t * direct callers such as the ProviderBackend facade share the same dedup as `getAccountForResource`.\n\t */\n\t@gate()\n\tasync getAccountForResourceResult(\n\t\tresource: T,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<Account | 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\ttry {\n\t\t\tconst account = await this.getProviderAccountForResource(session, resource);\n\t\t\tthis.resetRequestExceptionCount('getAccountForResource', connectionId);\n\t\t\treturn { value: account };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getAccountForResource', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t): Promise<Account | undefined>;\n\n\t@trace()\n\tasync getResourcesForUser(connectionId?: string): Promise<T[] | undefined> {\n\t\treturn (await this.getResourcesForUserResult(connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getResourcesForUser}. Recovers thrown errors into `{ error }` so callers\n\t * can surface them as warnings rather than silently swallowing them to `undefined`. Gated here (not on the\n\t * wrapper) so direct callers such as the ProviderBackend facade share the same dedup as `getResourcesForUser`.\n\t */\n\t@gate()\n\tasync getResourcesForUserResult(connectionId?: string): Promise<IntegrationResult<T[] | 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\ttry {\n\t\t\tconst resources = await this.getProviderResourcesForUser(session);\n\t\t\tthis.resetRequestExceptionCount('getResourcesForUser', connectionId);\n\t\t\treturn { value: resources };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getResourcesForUser', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderResourcesForUser(session: ProviderAuthenticationSession): Promise<T[] | undefined>;\n\n\t/**\n\t * Project discovery for a set of resources, returning the SDK collection `{ values, metadata }` (completeness\n\t * + per-resource failures) so callers can warn on failed resources and set `fetchFailed` without discarding\n\t * the resources that succeeded. Thrown errors are recovered into `{ error }` for the same reason: a caller\n\t * surfaces them as a warning rather than swallowing them to `undefined`.\n\t */\n\t@trace()\n\tasync getProjectsForResourcesWithMetadataResult(\n\t\tresources: T[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProviderApiCollectionResult<T> | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\tconst startingGeneration = this.discoveryGeneration;\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\t\t// Primary resolution can refresh its own session before discovery starts.\n\t\tif (!this.isResolvedDiscoverySessionCurrent(session, startingGeneration, connectionId)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst generation = this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud);\n\n\t\ttry {\n\t\t\tconst projects = await this.getProviderProjectsForResourcesWithMetadata(session, resources);\n\t\t\t// Existing cached readers keep their snapshot after invalidation without changing the replacement session.\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn { value: projects };\n\t\t\t}\n\n\t\t\tawait this.confirmScopedAuthFailures(session, projects.metadata);\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn { value: projects };\n\t\t\t}\n\n\t\t\tthis.resetRequestExceptionCount('getProjectsForResources', connectionId);\n\t\t\treturn { value: projects };\n\t\t} catch (ex) {\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.handleProviderException('getProjectsForResources', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Metadata-aware provider project discovery. The default wraps the array-returning\n\t * {@link getProviderProjectsForResources} in `{ values }`, so providers without a fan-out completeness signal\n\t * (Jira Data Center, Trello) need no change; a read that returned no data is `unknown`, not an empty project list.\n\t * Jira Cloud and Linear override it to preserve their completeness/failures.\n\t */\n\tprotected async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<ProviderApiCollectionResult<T>> {\n\t\treturn toProjectsCollection(await this.getProviderProjectsForResources(session, resources));\n\t}\n\n\tprotected abstract getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<T[] | undefined>;\n\n\t/**\n\t * The resources, and the projects of those `projectsOf` keeps, as the provider lists them now. For a read that must\n\t * not take a cached list for the current one: what it decides from this snapshot (that a selection is gone) would\n\t * be wrong for as long as the cache is stale. Shares nothing with the cached discovery: joins no read in flight,\n\t * reads and writes no cache, so it can neither be answered by a read started before what it needs to see, nor put\n\t * its answer into a cache a concurrent read relies on. `projects` is omitted when no resource list came back.\n\t */\n\t@trace()\n\tasync getUncachedDiscoveryResult(\n\t\tprojectsOf: (resource: T) => boolean,\n\t\tconnectionId?: string,\n\t): Promise<\n\t\tIntegrationResult<\n\t\t\t| {\n\t\t\t\t\tresources: T[] | undefined;\n\t\t\t\t\tprojects?: ProviderApiCollectionResult<T>;\n\t\t\t\t\tgeneration: number;\n\t\t\t\t\tsessionId: string;\n\t\t\t\t\tsessionCloud?: boolean;\n\t\t\t }\n\t\t\t| undefined\n\t\t>\n\t> {\n\t\tconst scope = getScopedLogger();\n\t\tconst startingGeneration = this.discoveryGeneration;\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null || isReadSessionFailure(session)) return session && { error: session.error };\n\t\tif (!this.isResolvedDiscoverySessionCurrent(session, startingGeneration, connectionId)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst generation = this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud);\n\n\t\ttry {\n\t\t\t// Reset on success as the cached reads are, so a refusal a re-sync healed doesn't count toward a disconnect.\n\t\t\tconst resources = await this.getProviderUncachedResourcesForUser(session);\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.resetRequestExceptionCount('getResourcesForUser', connectionId);\n\t\t\tif (resources == null) {\n\t\t\t\treturn {\n\t\t\t\t\tvalue: {\n\t\t\t\t\t\tresources: undefined,\n\t\t\t\t\t\tgeneration: generation,\n\t\t\t\t\t\tsessionId: session.id,\n\t\t\t\t\t\tsessionCloud: session.cloud,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst projects = await this.getProviderUncachedProjectsForResourcesWithMetadata(\n\t\t\t\tsession,\n\t\t\t\tresources.filter(projectsOf),\n\t\t\t);\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tawait this.confirmScopedAuthFailures(session, projects.metadata);\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.resetRequestExceptionCount('getProjectsForResources', connectionId);\n\t\t\treturn {\n\t\t\t\tvalue: {\n\t\t\t\t\tresources: resources,\n\t\t\t\t\tprojects: projects,\n\t\t\t\t\tgeneration: generation,\n\t\t\t\t\tsessionId: session.id,\n\t\t\t\t\tsessionCloud: session.cloud,\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tif (generation !== this.getSessionDiscoveryGeneration(session.id, connectionId, session.cloud)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.handleProviderException('getProjectsForResources', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * {@link getAccountForResourceResult} asked of the provider now, joining no lookup in flight and served from no\n\t * account it lends: a lookup started before the credential was revoked would vouch for it.\n\t */\n\t@trace()\n\tasync getUncachedAccountForResourceResult(\n\t\tresource: T,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<Account | 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 account = await this.getProviderUncachedAccountForResource(session, resource);\n\t\t\tthis.resetRequestExceptionCount('getAccountForResource', connectionId);\n\t\t\treturn { value: account };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getAccountForResource', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/** The account, asked of the provider; the default is {@link getProviderAccountForResource}, which lends none. */\n\tprotected getProviderUncachedAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t): Promise<Account | undefined> {\n\t\treturn this.getProviderAccountForResource(session, resource);\n\t}\n\n\t/**\n\t * The provider's resources, read past any cache it keeps and without storing into it. The default is\n\t * {@link getProviderResourcesForUser}, right for a provider that keeps none.\n\t */\n\tprotected getProviderUncachedResourcesForUser(session: ProviderAuthenticationSession): Promise<T[] | undefined> {\n\t\treturn this.getProviderResourcesForUser(session);\n\t}\n\n\t/**\n\t * The projects of `resources`, read past any cache the provider keeps and without storing into it. The default is\n\t * {@link getProviderProjectsForResourcesWithMetadata}, right for a provider that keeps none.\n\t */\n\tprotected getProviderUncachedProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<ProviderApiCollectionResult<T>> {\n\t\treturn this.getProviderProjectsForResourcesWithMetadata(session, resources);\n\t}\n\n\t@trace()\n\tasync getIssuesForProject(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getIssuesForProjectResult(project, options, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getIssuesForProject}. Recovers thrown errors into `{ error }` so callers\n\t * (e.g. the ProviderBackend facade) can surface a per-provider warning instead of a silent empty read —\n\t * important for providers that throw on unsupported operations (e.g. Linear's not-implemented issue read).\n\t */\n\tasync getIssuesForProjectResult(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\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\ttry {\n\t\t\tconst issues = await this.getProviderIssuesForProject(session, project, options);\n\t\t\tthis.resetRequestExceptionCount('getIssuesForProject', connectionId);\n\t\t\treturn { value: issues };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Truncation-aware variant of {@link getIssuesForProjectResult}. A provider that drains a project's issues\n\t * with an internal page backstop (Jira/Linear) overrides {@link getProviderIssuesForProjectWithTruncation}\n\t * to report when that backstop was hit, so the facade can surface an incomplete project read instead of\n\t * publishing it as complete. The default reports `truncated: false`.\n\t */\n\tasync getIssuesForProjectWithTruncationResult(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProjectIssuesDrain | 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.getProviderIssuesForProjectWithTruncation(session, project, options);\n\t\t\tawait this.confirmScopedAuthFailures(session, result?.metadata);\n\t\t\tthis.resetRequestExceptionCount('getIssuesForProject', connectionId);\n\t\t\treturn { value: result };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Reads several projects' issues, one result per request in request order, each with the contract of\n\t * {@link getIssuesForProjectWithTruncationResult}, so one project failing leaves its siblings readable.\n\t *\n\t * A tracker that can read several projects in one query (Jira, Linear) groups them through\n\t * {@link getProjectIssuesSearches}, so a project with none of the user's issues costs no request of its own.\n\t * Every other request, and every project of a search that did not complete, is read on its own.\n\t *\n\t * A search's pages are global to its project set, so only a search that completes says something about each\n\t * of its projects: one that does not, or that fails for a reason that may be one project's\n\t * ({@link failsEveryScope}), is read again project by project, which reports completeness, failures and\n\t * retries per project. Only a failure every project would hit on its own read too fails them all at once.\n\t */\n\tasync getIssuesForProjectsWithTruncationResult(\n\t\trequests: readonly ProjectIssuesRequest<T>[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProjectIssuesDrain | undefined>[]> {\n\t\tconst searches = this.getProjectIssuesSearches(requests);\n\t\tif (searches == null) return this.readProjectsOneByOne(requests, connectionId);\n\n\t\tconst scope = getScopedLogger();\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null || isReadSessionFailure(session)) {\n\t\t\treturn requests.map(() => session && { error: session.error });\n\t\t}\n\n\t\tconst results: IntegrationResult<ProjectIssuesDrain | undefined>[] = requests.map(() => undefined);\n\t\tconst unsearchedIndices: number[] = [];\n\t\tawait mapBounded(searches, providerFanOutConcurrency, async indices => {\n\t\t\ttry {\n\t\t\t\tconst values = await this.searchProviderProjectIssues(\n\t\t\t\t\tsession,\n\t\t\t\t\tindices.map(index => requests[index]),\n\t\t\t\t);\n\t\t\t\tif (values == null) {\n\t\t\t\t\tunsearchedIndices.push(...indices);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.resetRequestExceptionCount('getIssuesForProject', connectionId);\n\t\t\t\tindices.forEach((index, i) => {\n\t\t\t\t\tresults[index] = { value: { values: values[i], truncated: false } };\n\t\t\t\t});\n\t\t\t} catch (ex) {\n\t\t\t\tif (!failsEveryScope(ex)) {\n\t\t\t\t\tLogger.warn(scope, `Project search failed (${String(ex)}); reading its projects one by one`);\n\t\t\t\t\tunsearchedIndices.push(...indices);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\t\tconst error = toError(ex);\n\t\t\t\tfor (const index of indices) {\n\t\t\t\t\tresults[index] = { error: error };\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// After every search rather than inside each, so the fallback reads share one concurrency bound.\n\t\tif (unsearchedIndices.length > 0) {\n\t\t\tconst fallback = await this.readProjectsOneByOne(\n\t\t\t\tunsearchedIndices.map(index => requests[index]),\n\t\t\t\tconnectionId,\n\t\t\t);\n\t\t\tunsearchedIndices.forEach((index, i) => {\n\t\t\t\tresults[index] = fallback[i];\n\t\t\t});\n\t\t}\n\t\treturn results;\n\t}\n\n\t/**\n\t * Groups requests into searches, as lists of request indices, that {@link searchProviderProjectIssues} can read\n\t * in one query each. Undefined reads every project on its own, which is the default.\n\t */\n\tprotected getProjectIssuesSearches(_requests: readonly ProjectIssuesRequest<T>[]): number[][] | undefined {\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Reads the projects of one search in one query: each project's issues in request order, or undefined when the\n\t * query did not complete, which reads them one by one instead. A throw is classified by {@link failsEveryScope}.\n\t */\n\tprotected searchProviderProjectIssues(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_requests: readonly ProjectIssuesRequest<T>[],\n\t): Promise<IssueShape[][] | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\tprivate readProjectsOneByOne(\n\t\trequests: readonly ProjectIssuesRequest<T>[],\n\t\tconnectionId: string | undefined,\n\t): Promise<IntegrationResult<ProjectIssuesDrain | undefined>[]> {\n\t\treturn mapBounded(requests, providerFanOutConcurrency, async request => {\n\t\t\ttry {\n\t\t\t\treturn await this.getIssuesForProjectWithTruncationResult(\n\t\t\t\t\trequest.project,\n\t\t\t\t\trequest.options,\n\t\t\t\t\tconnectionId,\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\treturn { error: toError(ex) };\n\t\t\t}\n\t\t});\n\t}\n\n\tprotected abstract getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined>;\n\n\t/**\n\t * Truncation-aware core of {@link getProviderIssuesForProject}. The default wraps the plain read and\n\t * reports `truncated: false`; a provider whose per-project drain is capped by a page backstop overrides\n\t * this to report incompleteness. Optional metadata lets providers surface structured per-project failures\n\t * (e.g. a page-level auth rejection) without discarding the already-fetched prefix.\n\t */\n\tprotected async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tconst values = await this.getProviderIssuesForProject(session, project, options);\n\t\tif (values == null) return undefined;\n\t\treturn { values: values, truncated: false };\n\t}\n}\n\n/** A project list as a collection: no data is no list, `unknown`, never an empty complete one. */\nexport function toProjectsCollection<T extends ResourceDescriptor>(\n\tprojects: T[] | undefined,\n): ProviderApiCollectionResult<T> {\n\treturn projects != null ? { values: projects } : { values: [], metadata: { completeness: 'unknown' } };\n}\n"],"mappings":"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"}
|
|
@@ -6,14 +6,14 @@ import { trace } from "../../../../utils/decorators/log.js";
|
|
|
6
6
|
import { PromiseCache } from "../../../../utils/promiseCache.js";
|
|
7
7
|
import __decorate from "../../../../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
|
|
8
8
|
import { AuthenticationError, RequestClientError, RequestNotFoundError } from "../../../../git/errors.js";
|
|
9
|
+
import { sha256, uuid } from "../../../../utils/crypto.js";
|
|
9
10
|
import { maybeStopWatch } from "../../../../utils/stopwatch.js";
|
|
10
11
|
import { base64 } from "../../../../utils/base64.js";
|
|
11
|
-
import { sha256, uuid } from "../../../../utils/crypto.js";
|
|
12
12
|
import { effectiveIssueSort } from "../../../../git/utils/issue.utils.js";
|
|
13
13
|
import { ProviderFetchError, isRateLimitResponse, toRateLimitError } from "../../errors.js";
|
|
14
14
|
import { throwProviderError } from "../providerErrors.js";
|
|
15
|
-
import { decodePathSegment } from "../../utils/domain.utils.js";
|
|
16
15
|
import { azureWorkItemSearchResultLimit, parseAzureWorkItemSearchCursor, toAzureSearchPageSize, toAzureWorkItemSearchCursorKey, toAzureWorkItemSearchQueries, toAzureWorkItemSearchWiql } from "./search.js";
|
|
16
|
+
import { decodePathSegment } from "../../utils/domain.utils.js";
|
|
17
17
|
import { azurePullRequestStatusToState, azureWorkItemsStateCategoryToState, encodeAzurePathSegment, fromAzurePullRequest, fromAzureWorkItem, getAzurePullRequestWebUrl, isClosedAzurePullRequestStatus, normalizeAzureBranchName, parseAzureWorkItemDate, sanitizeAzureRepositoryUrl } from "./models.js";
|
|
18
18
|
import { AzureWorkItemDescriptionsCache } from "./workItemDescriptions.js";
|
|
19
19
|
import { AzureWorkItemStatesCache, getAzureWorkItemTypeStatesPath, getAzureWorkItemTypesUrl, requireAzureWorkItemStateCategories, resolveAzureWorkItemStateCategory } from "./workItemStateCategories.js";
|
|
@@ -6,10 +6,10 @@ import "../../../git/models/pullRequest.js";
|
|
|
6
6
|
import "../constants.js";
|
|
7
7
|
import { throwProviderError } from "./providerErrors.js";
|
|
8
8
|
import { throwIfCallerContractError, toCollectionScopeFailure } from "../collectionMetadata.js";
|
|
9
|
-
import { toTokenWithInfo } from "../authentication/models.js";
|
|
10
9
|
import "../providerFilters.js";
|
|
11
10
|
import { collectProviderPagedResult, flatSettledResultsOrThrow, mergeCollectionMetadata, resolveBranchPullRequests, selectBranchPullRequests } from "./utils/providerPaging.js";
|
|
12
11
|
import { fromProviderIssue, fromProviderPullRequest, getProviderPullRequestIdentity, isNativePullRequest, issueNamesPeople, providerPullRequestMatchesSearch, providersMetadata, pullRequestNamesPeople, toIssueShape, toProviderIssueStates, toProviderPullRequestStates } from "./models.js";
|
|
12
|
+
import { toTokenWithInfo } from "../authentication/models.js";
|
|
13
13
|
import { toAzurePullRequestEtagFields, toAzureWorkItemEtagFields } from "./azure/models.js";
|
|
14
14
|
import { decodeAzurePathSegment, getAzurePullRequestIdentityFromMaybeUrl, getAzureRepositoryIdentity } from "./azure/azure.utils.js";
|
|
15
15
|
import { GitHostIntegration } from "../models/gitHostIntegration.js";
|
|
@@ -8,9 +8,9 @@ import "../../../git/models/pullRequest.js";
|
|
|
8
8
|
import { IntegrationReadUnavailableError } from "../errors.js";
|
|
9
9
|
import "../constants.js";
|
|
10
10
|
import { toCollectionScopeFailure } from "../collectionMetadata.js";
|
|
11
|
-
import { toTokenWithInfo } from "../authentication/models.js";
|
|
12
11
|
import { compareAzurePullRequestSearchPositions, getInvalidAzureSearchDate, isAzureWorkItemSearchFirstPage, parseAzurePullRequestSearchCursor, toAzurePullRequestSearchCursorKey, toAzurePullRequestSearchFilter, toAzurePullRequestSearchPosition, toAzureSearchPageSize } from "./azure/search.js";
|
|
13
12
|
import { fromProviderPullRequest, getProviderPullRequestIdentity, providersMetadata, pullRequestNamesPeople, toProviderPullRequestStates } from "./models.js";
|
|
13
|
+
import { toTokenWithInfo } from "../authentication/models.js";
|
|
14
14
|
import { getAzureRepositoryIdentity } from "./azure/azure.utils.js";
|
|
15
15
|
import { discoveryCacheTtl } from "./utils/discoveryCache.js";
|
|
16
16
|
import { AzureDevOpsIntegrationBase, findAzureRepositoryApiBaseUrl, getAzureRepositoryApiBaseUrl, getAzureScopeVirtualDirectory, isAzureProjectlessCandidate, markMyGroupReviews, resolveAzureProjectlessRepository, sameAzureName, toAzureReviewerReads, uniqueAzureNames } from "./azureDevOps.js";
|
|
@@ -467,7 +467,7 @@ var BitbucketApi = class {
|
|
|
467
467
|
throw new AuthenticationError(tokenInfo, 3, ex);
|
|
468
468
|
case 500:
|
|
469
469
|
scope?.error(ex);
|
|
470
|
-
if (ex.response != null) reportRequestFailure(provider, ex, () => this.config.onRequestFailed?.(provider == null || provider.id === "bitbucket" ? l10n.t("{0} failed to respond and might be experiencing issues. Please visit the [Bitbucket status page](https://bitbucket.status.atlassian.com/) for more information.", provider?.name ?? "Bitbucket") : l10n.t("{0} failed to respond and might be experiencing issues.", provider.name)), deferFailure);
|
|
470
|
+
if (ex.response != null) reportRequestFailure(provider, ex, () => this.config.onRequestFailed?.(provider == null || provider.id === "bitbucket" ? l10n.t("{0} failed to respond and might be experiencing issues. Please visit the [Bitbucket status page](https://bitbucket.status.atlassian.com/) for more information.", provider?.name ?? "Bitbucket") : l10n.t("{0} failed to respond and might be experiencing issues.", provider.name)), deferFailure, token.connectionId);
|
|
471
471
|
return;
|
|
472
472
|
case 502:
|
|
473
473
|
scope?.error(ex);
|