@gitkraken/core-gitlens 0.10.3 → 0.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/dist/git/models/pullRequest.d.ts +37 -0
  3. package/dist/git/models/pullRequest.js.map +1 -1
  4. package/dist/git-cli/providers/graph.js +14 -8
  5. package/dist/git-cli/providers/graph.js.map +1 -1
  6. package/dist/plus/git-github/api/github.d.ts +3 -1
  7. package/dist/plus/git-github/api/github.js.map +1 -1
  8. package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts +2 -1
  9. package/dist/plus/integrations/authentication/configuredIntegrationService.js +11 -6
  10. package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
  11. package/dist/plus/integrations/authentication/models.d.ts +11 -0
  12. package/dist/plus/integrations/authentication/models.js.map +1 -1
  13. package/dist/plus/integrations/constants.d.ts +4 -0
  14. package/dist/plus/integrations/constants.js.map +1 -1
  15. package/dist/plus/integrations/integrationService.d.ts +6 -0
  16. package/dist/plus/integrations/integrationService.js +34 -5
  17. package/dist/plus/integrations/integrationService.js.map +1 -1
  18. package/dist/plus/integrations/models/gitHostIntegration.d.ts +5 -1
  19. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  20. package/dist/plus/integrations/models/integration.d.ts +7 -1
  21. package/dist/plus/integrations/models/integration.js +12 -2
  22. package/dist/plus/integrations/models/integration.js.map +1 -1
  23. package/dist/plus/integrations/providers/bitbucket.js +2 -1
  24. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  25. package/dist/plus/integrations/providers/github.d.ts +12 -1
  26. package/dist/plus/integrations/providers/github.js +41 -1
  27. package/dist/plus/integrations/providers/github.js.map +1 -1
  28. package/dist/plus/integrations/providers/gitlab.d.ts +1 -1
  29. package/dist/plus/integrations/providers/gitlab.js +3 -2
  30. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  31. package/dist/plus/integrations/providers/linear.d.ts +9 -1
  32. package/dist/plus/integrations/providers/linear.js +34 -11
  33. package/dist/plus/integrations/providers/linear.js.map +1 -1
  34. package/dist/plus/integrations/providers/models.js +1 -0
  35. package/dist/plus/integrations/providers/models.js.map +1 -1
  36. package/dist/utils/cancellation.d.ts +2 -3
  37. package/dist/utils/cancellation.js +2 -3
  38. package/dist/utils/cancellation.js.map +1 -1
  39. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"gitlab.js","names":[],"sources":["../../../../../plus/integrations/src/providers/gitlab.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScopeFailure } from '@gitkraken/provider-apis';\nimport * as l10n from '@vscode/l10n';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type {\n\tPullRequest,\n\tPullRequestMergeMethod,\n\tPullRequestShape,\n\tPullRequestState,\n\tPullRequestStateFilter,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { RepositoryDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport type { PullRequestUrlIdentity } from '@gitlens/git/utils/pullRequest.utils.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport type { Emitter } from '@gitlens/utils/event.js';\nimport { uniqueBy } from '@gitlens/utils/iterable.js';\nimport { batch, mapSettledBounded } from '@gitlens/utils/promise.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport { IntegrationReadUnavailableError } from '../errors.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport type { SearchMyPullRequestsOptions, SearchPullRequestsOptions } from '../models/gitHostIntegration.js';\nimport { GitHostIntegration } from '../models/gitHostIntegration.js';\nimport type {\n\tAccountWideIssuesResult,\n\tIssueEtagFields,\n\tIssueEtagInclude,\n\tPullRequestEtagFields,\n\tPullRequestEtagInclude,\n\tSearchMyIssuesOptions,\n} from '../models/integration.js';\nimport type { GitLabIntegrationIds } from './gitlab/gitlab.utils.js';\nimport { getGitLabPullRequestIdentityFromMaybeUrl, matchesGitLabOrgNamespace } from './gitlab/gitlab.utils.js';\nimport {\n\tfromGitLabMergeRequestProvidersApi,\n\tgitLabEtagFieldsMaxIids,\n\tgitLabEtagFieldsWithChecksMaxIids,\n\ttoGitLabIssueEtagFields,\n\ttoGitLabPullRequestEtagFields,\n} from './gitlab/models.js';\nimport type {\n\tProviderApiPagedResult,\n\tProviderHierarchyResult,\n\tProviderOrganization,\n\tProviderPullRequest,\n\tProviderRepository,\n} from './models.js';\nimport {\n\tfromProviderPullRequest,\n\tgetProviderPullRequestIdentity,\n\tIssueFilter,\n\tProviderPullRequestReviewState,\n\tprovidersMetadata,\n\tPullRequestFilter,\n\ttoIssueShape,\n\ttoProviderPullRequestStates,\n} from './models.js';\nimport type { ProvidersApi } from './providersApi.js';\nimport {\n\tcollectProviderPagedResult,\n\tmergeCollectionMetadata,\n\treadEtagFieldsByRepository,\n\tresolveBranchPullRequests,\n} from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[GitCloudHostIntegrationId.GitLab];\nconst authProvider: IntegrationAuthenticationProviderDescriptor = Object.freeze({\n\tid: metadata.id,\n\tscopes: metadata.scopes,\n});\n\nconst cloudEnterpriseMetadata = providersMetadata[GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted];\nconst cloudEnterpriseAuthProvider: IntegrationAuthenticationProviderDescriptor = Object.freeze({\n\tid: cloudEnterpriseMetadata.id,\n\tscopes: cloudEnterpriseMetadata.scopes,\n});\n\ntype GitLabPullRequestAssociation = 'assigned' | 'authored' | 'reviewRequested';\ntype GitLabPullRequestFacetCursor = Partial<Record<GitLabPullRequestAssociation, string>>;\n\n/**\n * The account-wide relationships GitLab can express, and the `scope` each maps to. `undefined` means GitLab has\n * no equivalent axis (it has neither `Mention` nor `reviewed-by`), and this map is the single declaration of\n * that. Exhaustive over `PullRequestFilter` on purpose: a new member must be decided for GitLab here rather\n * than compiling through as silently unsupported. Exported for the advertised-vs-expressible capability test,\n * which is what keeps it agreeing with `supportedAccountWidePullRequestFilters`; no runtime consumer imports it.\n */\nexport const gitLabAssociationForFilter: Record<PullRequestFilter, GitLabPullRequestAssociation | undefined> = {\n\t[PullRequestFilter.Assignee]: 'assigned',\n\t[PullRequestFilter.Author]: 'authored',\n\t[PullRequestFilter.ReviewRequested]: 'reviewRequested',\n\t[PullRequestFilter.Reviewed]: undefined,\n\t[PullRequestFilter.Mention]: undefined,\n};\n\nfunction parseGitLabPullRequestFacetCursor(cursor: string | undefined): GitLabPullRequestFacetCursor {\n\tif (!cursor) return {};\n\n\ttry {\n\t\tconst parsed = JSON.parse(cursor) as { type?: unknown; cursors?: unknown };\n\t\tif (parsed.type !== 'gitlab-associations' || parsed.cursors == null || typeof parsed.cursors !== 'object') {\n\t\t\treturn {};\n\t\t}\n\n\t\tconst cursors = parsed.cursors as Record<string, unknown>;\n\t\treturn Object.fromEntries(\n\t\t\t(['assigned', 'authored', 'reviewRequested'] as const)\n\t\t\t\t.filter(association => typeof cursors[association] === 'string' && cursors[association] !== '')\n\t\t\t\t.map(association => [association, cursors[association] as string]),\n\t\t);\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nexport type GitLabRepositoryDescriptor = RepositoryDescriptor;\n\n/** How many SSH signing-key lookups (account resolve + keys fetch) to run concurrently, to avoid a request burst. */\nconst sshSigningKeyResolveBatchSize = 10;\n\nabstract class GitLabIntegrationBase<ID extends GitLabIntegrationIds> extends GitHostIntegration<\n\tID,\n\tGitLabRepositoryDescriptor\n> {\n\tprotected abstract apiBaseUrlFor(session: ProviderAuthenticationSession): string;\n\n\t/** The installation base the PAT-based reads take on self-hosted GitLab; gitlab.com passes none. */\n\tprotected enterpriseBaseUrlFor(session: ProviderAuthenticationSession): string | undefined {\n\t\treturn this.isEnterprise ? this.getSelfManagedApiBaseUrl(session) : undefined;\n\t}\n\n\t/** Self-hosted GitLab uses PAT semantics and a domain-based API base; gitlab.com does not. */\n\tprotected get isEnterprise(): boolean {\n\t\treturn this.id === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted;\n\t}\n\n\tprotected override async getProviderAccountForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getAccountForCommit(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\trev,\n\t\t\t{\n\t\t\t\t...options,\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderAccountForEmail(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\temail: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getAccountForEmail(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\temail,\n\t\t\t{\n\t\t\t\t...options,\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderSshSigningKeysForEmails(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\temails: string[],\n\t): Promise<Map<string, string[]>> {\n\t\tconst result = new Map<string, string[]>();\n\n\t\tconst api = await this.authenticationService.apis.gitlab;\n\t\tif (api == null) return result;\n\n\t\tconst token = toTokenWithInfo(this.id, session);\n\n\t\t// GitLab resolves users one email at a time (no batch search). Run them in bounded batches rather than firing all\n\t\t// (up to providerVerifyLimit) account + key lookups at once, to avoid a request burst that trips rate limiting.\n\t\tawait batch(emails, sshSigningKeyResolveBatchSize, async email => {\n\t\t\tconst account = await this.getProviderAccountForEmail(session, repo, email);\n\t\t\tif (account?.id == null) return;\n\n\t\t\tconst keys = await api.getUserSigningKeys(this, token, account.id, {\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t});\n\t\t\tresult.set(\n\t\t\t\temail.toLowerCase(),\n\t\t\t\tkeys.map(k => k.key),\n\t\t\t);\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tprotected override async getProviderDefaultBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t): Promise<DefaultBranch | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getDefaultBranch(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\t{ id }: { id: string; key: string },\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getIssueOrPullRequest(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tNumber(id),\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.authenticationService.apis.gitlab;\n\t\tconst providerApi = await this.getProvidersApi();\n\t\tif (!api || !repo || !id) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst repoId = await api.getProjectId(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tthis.apiBaseUrlFor(session),\n\t\t\tundefined,\n\t\t);\n\t\tif (!repoId) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst apiResult = await providerApi.getIssue(\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t{ namespace: repo.owner, name: repo.name, number: id },\n\t\t\t{\n\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t\tconst issue = apiResult != null ? toIssueShape(apiResult, this, { projection: 'point' }) : undefined;\n\t\treturn issue != null ? { ...issue, type: 'issue' } : undefined;\n\t}\n\n\tprotected override async getProviderPullRequestForBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\tbranch: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t\tinclude?: PullRequestState[];\n\t\t},\n\t): Promise<PullRequest | undefined> {\n\t\tconst { include, ...opts } = options ?? {};\n\n\t\tconst toGitLabMergeRequestState = (await import(/* webpackChunkName: \"integrations\" */ './gitlab/models.js'))\n\t\t\t.toGitLabMergeRequestState;\n\t\treturn (await this.authenticationService.apis.gitlab)?.getPullRequestForBranch(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tbranch,\n\t\t\t{\n\t\t\t\t...opts,\n\t\t\t\tinclude: include?.map(s => toGitLabMergeRequestState(s)),\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\trev: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getPullRequestForCommit(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\trev,\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: GitLabRepositoryDescriptor,\n\t\tid: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getPullRequest(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tresource.owner,\n\t\t\tresource.name,\n\t\t\tparseInt(id, 10),\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * One request per target, settled independently so one target's failure rejects only its own slot; through the\n\t * SDK's issue read and the conversion the repo-scoped list rows take. GitLens' own GitLab client can't alias\n\t * the read instead: it fails a whole document on any GraphQL error, where a batch needs per-target errors.\n\t */\n\tprotected override async getProviderIssuesBatch(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcoordinates: readonly { owner: string; repo: string; number: number; project?: string }[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<IssueShape | undefined>[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst baseUrl = this.enterpriseBaseUrlFor(session);\n\t\t// The confirming read must ask the same host, over the same protocol, as the read it confirms.\n\t\tconst confirmBaseUrl = this.apiBaseUrlFor(session);\n\n\t\treturn mapSettledBounded(coordinates, providerFanOutConcurrency, async c => {\n\t\t\tconst issue = await api.getIssueForRepo(tokenWithInfo, { namespace: c.owner, name: c.repo }, c.number, {\n\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\tbaseUrl: baseUrl,\n\t\t\t});\n\t\t\tif (issue != null) {\n\t\t\t\tconst shape = toIssueShape(issue, this, { projection: 'batch' });\n\t\t\t\tif (shape == null) throw new Error(`GitLab returned issue ${c.number} without a URL or update time`);\n\n\t\t\t\treturn shape;\n\t\t\t}\n\n\t\t\t// provider-apis' \"not found\" can come from a reply carrying only GraphQL errors (see `getIssueForRepo`),\n\t\t\t// so only our own strict read can prove the miss.\n\t\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\t\tif (gitlab == null) {\n\t\t\t\tthrow new IntegrationReadUnavailableError(this.name, 'cannot confirm a missing issue');\n\t\t\t}\n\n\t\t\tconst exists = await gitlab.hasIssue(\n\t\t\t\tthis,\n\t\t\t\ttokenWithInfo,\n\t\t\t\tc.owner,\n\t\t\t\tc.repo,\n\t\t\t\tc.number,\n\t\t\t\t{ baseUrl: confirmBaseUrl, deferFailure: true },\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\t// Our own query can't produce the list rows' shape, so a disagreement fails rather than answering thinner.\n\t\t\tif (exists) throw new Error(`GitLab reported issue ${c.number} missing, then found it`);\n\n\t\t\treturn undefined;\n\t\t});\n\t}\n\n\t/**\n\t * One request per target, settled independently so one target's failure rejects only its own slot; through\n\t * the same SDK read and conversion the manager's list rows take.\n\t */\n\tprotected override async getProviderPullRequestsBatch(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcoordinates: readonly { owner: string; repo: string; number: number; project?: string }[],\n\t\toptions: { currentAccount?: { id: string; username?: string } } | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<PullRequestShape | undefined>[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst baseUrl = this.enterpriseBaseUrlFor(session);\n\t\t// The confirming read must ask the same host, over the same protocol, as the read it confirms.\n\t\tconst confirmBaseUrl = this.apiBaseUrlFor(session);\n\n\t\treturn mapSettledBounded(coordinates, providerFanOutConcurrency, async c => {\n\t\t\tconst pr = await api.getPullRequestForRepo(tokenWithInfo, { namespace: c.owner, name: c.repo }, c.number, {\n\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\tbaseUrl: baseUrl,\n\t\t\t});\n\t\t\tif (pr != null) {\n\t\t\t\treturn fromProviderPullRequest(pr, this, {\n\t\t\t\t\tcurrentAccount: options?.currentAccount,\n\t\t\t\t\tprojection: 'batch',\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// provider-apis' GitLab GraphQL helper ignores the response's `errors`, so a reply carrying only errors\n\t\t\t// (e.g. a server-side timeout) comes back as `null`, the same as not-found. Our own client throws on\n\t\t\t// `errors`, and `strict` makes it answer `undefined` only for a real miss.\n\t\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\t\tif (gitlab == null) {\n\t\t\t\tthrow new IntegrationReadUnavailableError(this.name, 'cannot confirm a missing merge request');\n\t\t\t}\n\n\t\t\tconst confirmed = await gitlab.getPullRequest(\n\t\t\t\tthis,\n\t\t\t\ttokenWithInfo,\n\t\t\t\tc.owner,\n\t\t\t\tc.repo,\n\t\t\t\tc.number,\n\t\t\t\t{ baseUrl: confirmBaseUrl, strict: true, deferFailure: true },\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\t// Our own client's row can't produce the list rows' shape (`id` is the iid, not the global id; no\n\t\t\t// `authoredByMe`), so a disagreement fails rather than answering with a different identity.\n\t\t\tif (confirmed != null) throw new Error(`GitLab reported pull request ${c.number} missing, then found it`);\n\n\t\t\treturn undefined;\n\t\t});\n\t}\n\n\t/**\n\t * The cheap check behind the batch issue read's etags: GitLens' own GitLab client reads each project's issues by\n\t * iid, up to {@link gitLabEtagFieldsMaxIids} in ONE request, where {@link getProviderIssuesBatch} sends one or\n\t * two per target. Each row is converted as provider-apis and `toIssueShape` convert the full one, so both reads\n\t * compute the same etag, and `'reactions'` adds the upvote count the full row's `thumbsUpCount` comes from. A\n\t * request that throws rejects only its own targets' slots.\n\t */\n\tprotected override async getProviderIssuesEtagFields(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcoordinates: readonly { owner: string; repo: string; number: number; project?: string }[],\n\t\toptions: { etagIncludes?: readonly IssueEtagInclude[] },\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined> {\n\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\tif (gitlab == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t// The host and protocol the full read's confirming read asks.\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\t\tconst etagIncludes = options.etagIncludes ?? [];\n\n\t\treturn readEtagFieldsByRepository(\n\t\t\tcoordinates,\n\t\t\tgitLabEtagFieldsMaxIids,\n\t\t\t(owner, repo, iids) =>\n\t\t\t\tgitlab.getIssuesEtagFields(\n\t\t\t\t\tthis,\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t`${owner}/${repo}`,\n\t\t\t\t\tiids,\n\t\t\t\t\t{ baseUrl: baseUrl, etagIncludes: etagIncludes, deferFailure: true },\n\t\t\t\t\tcancellation,\n\t\t\t\t),\n\t\t\tnode => toGitLabIssueEtagFields(node, etagIncludes),\n\t\t);\n\t}\n\n\t/**\n\t * The cheap check behind the batch pull request read's etags: {@link getProviderIssuesEtagFields}'s requests and\n\t * failure isolation over merge requests, selecting a merge request's change state plus the fields of each of\n\t * `options.etagIncludes`. Every include is meaningful here: a full row carries a mergeability, a review decision\n\t * and a check rollup, each of which can change without moving `updatedAt`.\n\t */\n\tprotected override async getProviderPullRequestsEtagFields(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcoordinates: readonly { owner: string; repo: string; number: number; project?: string }[],\n\t\toptions: { etagIncludes?: readonly PullRequestEtagInclude[] },\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<PullRequestEtagFields | undefined>[] | undefined> {\n\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\tif (gitlab == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t// The host and protocol the full read's confirming read asks.\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\t\tconst etagIncludes = options.etagIncludes ?? [];\n\n\t\treturn readEtagFieldsByRepository(\n\t\t\tcoordinates,\n\t\t\tetagIncludes.includes('checks') ? gitLabEtagFieldsWithChecksMaxIids : gitLabEtagFieldsMaxIids,\n\t\t\t(owner, repo, iids) =>\n\t\t\t\tgitlab.getMergeRequestsEtagFields(\n\t\t\t\t\tthis,\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t`${owner}/${repo}`,\n\t\t\t\t\tiids,\n\t\t\t\t\t{ baseUrl: baseUrl, etagIncludes: etagIncludes, deferFailure: true },\n\t\t\t\t\tcancellation,\n\t\t\t\t),\n\t\t\tnode => toGitLabPullRequestEtagFields(node, etagIncludes),\n\t\t);\n\t}\n\n\t/**\n\t * Two steps, because provider-apis has no source-branch filter: GitLens' own GitLab client finds each branch's\n\t * matching iids, one request per target, then {@link getProviderPullRequestsBatch} resolves them — so a row is\n\t * the one `getPullRequestsBatch` returns for that merge request, `url` and `authoredByMe` included.\n\t */\n\tprotected override async getProviderPullRequestsForBranches(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly { owner: string; repo: string; project?: string; branch: string; headOwner?: string }[],\n\t\toptions: { currentAccount?: { id: string; username?: string }; limit: number },\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<{ pullRequests: PullRequestShape[]; truncated: boolean }>[] | undefined> {\n\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\tif (gitlab == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t// The session's own host and protocol, as the batch read's confirming read uses.\n\t\tconst apiBaseUrl = this.apiBaseUrlFor(session);\n\n\t\tconst found = await mapSettledBounded(targets, providerFanOutConcurrency, t =>\n\t\t\tgitlab.getPullRequestNumbersForBranch(\n\t\t\t\tthis,\n\t\t\t\ttokenWithInfo,\n\t\t\t\tt.owner,\n\t\t\t\tt.repo,\n\t\t\t\tt.branch,\n\t\t\t\t{ baseUrl: apiBaseUrl, headOwner: t.headOwner, limit: options.limit, deferFailure: true },\n\t\t\t\tcancellation,\n\t\t\t),\n\t\t);\n\t\treturn resolveBranchPullRequests(targets, found, coordinates =>\n\t\t\tthis.getProviderPullRequestsBatch(\n\t\t\t\tsession,\n\t\t\t\tcoordinates,\n\t\t\t\t{ currentAccount: options.currentAccount },\n\t\t\t\tcancellation,\n\t\t\t),\n\t\t);\n\t}\n\n\tpublic override async getRepoInfo(repo: {\n\t\towner: string;\n\t\tname: string;\n\t\tproject?: string;\n\t\tconnectionId?: string;\n\t}): Promise<ProviderRepository | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSessionOrThrow(repo.connectionId, undefined);\n\t\tif (session == null) return undefined;\n\n\t\treturn api.getRepo(toTokenWithInfo(this.id, session), repo.owner, repo.name, repo.project, {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t});\n\t}\n\n\tprotected override async getProviderRepositoryMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getRepositoryMetadata(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async getProviderOrganizationsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst result = await api.getGitlabGroupsForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t});\n\t\treturn {\n\t\t\tvalues: result.values.map(g => ({ id: g.id, providerId: this.id, name: g.fullPath, url: g.webUrl })),\n\t\t\t...(result.truncated ? { truncated: true } : {}),\n\t\t\t...(result.metadata != null ? { metadata: result.metadata } : {}),\n\t\t};\n\t}\n\n\t/**\n\t * GitLab has no group-scoped repos endpoint, so each page is a page of the current user's member repos\n\t * across all namespaces, filtered to `org` and its subgroups (mirroring gkcli's `gitlabOrgMatches`).\n\t * Follow `paging.cursor` to page the full list; repos the user isn't a member of aren't returned.\n\t * Because the filter is applied per page, a page can come back with `values: []` while\n\t * `paging.more` is still `true` (e.g. a page full of repos outside `org`) — don't treat an empty\n\t * page as the end of pagination, only `paging.more`/`collectPagedResults` reaching exhaustion.\n\t */\n\tprotected override async getProviderRepositoriesForOrg(\n\t\tsession: ProviderAuthenticationSession,\n\t\torg: string,\n\t\toptions?: { cursor?: string },\n\t): Promise<ProviderHierarchyResult<ProviderRepository> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst result = await api.getReposForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\tcursor: options?.cursor,\n\t\t});\n\t\treturn {\n\t\t\tvalues: result.values.filter(r => matchesGitLabOrgNamespace(r.namespace, org)),\n\t\t\t...(result.paging != null ? { paging: result.paging } : {}),\n\t\t};\n\t}\n\n\tprotected override async getProviderRepositoriesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: { cursor?: string },\n\t): Promise<ProviderHierarchyResult<ProviderRepository> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// GitLab's membership read is already user-affiliated, so the account-wide walk is the unfiltered\n\t\t// version of the per-org read above (which pages the same source and filters by namespace).\n\t\treturn api.getReposForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\tcursor: options?.cursor,\n\t\t});\n\t}\n\n\tprotected override async searchProviderMyPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: GitLabRepositoryDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t\toptions?: SearchMyPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// Resolve the username from THIS session's token (multi-account: `session` may be a non-primary\n\t\t// connection); `getCurrentAccount()` would use the primary `_session` and filter by the wrong user.\n\t\tconst username = (await this.getProviderCurrentAccount(session))?.username;\n\t\tif (!username) {\n\t\t\treturn Promise.resolve([]);\n\t\t}\n\n\t\tconst apiResult = await api.getPullRequestsForUser(toTokenWithInfo(this.id, session), username, {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\tstates: toProviderPullRequestStates(options?.state),\n\t\t});\n\n\t\tif (apiResult == null) {\n\t\t\treturn Promise.resolve([]);\n\t\t}\n\n\t\t// now I'm going to filter prs from the result according to the repos parameter\n\t\tlet prs;\n\t\tif (repos != null) {\n\t\t\tconst repoMap = new Map<string, GitLabRepositoryDescriptor>();\n\t\t\tfor (const repo of repos) {\n\t\t\t\trepoMap.set(`${repo.owner}/${repo.name}`, repo);\n\t\t\t}\n\t\t\tprs = apiResult.values.filter(pr => {\n\t\t\t\tconst repo = repoMap.get(`${pr.repository.owner.login}/${pr.repository.name}`);\n\t\t\t\treturn repo != null;\n\t\t\t});\n\t\t} else {\n\t\t\tprs = apiResult.values;\n\t\t}\n\n\t\tconst results: IterableIterator<PullRequest> = uniqueBy(\n\t\t\t[\n\t\t\t\t...prs\n\t\t\t\t\t.filter(pr => {\n\t\t\t\t\t\tconst isAssignee = pr.assignees?.some(a => a.username === username);\n\t\t\t\t\t\tconst isRequestedReviewer = pr.reviews?.some(\n\t\t\t\t\t\t\t// Match only reviews assigned to the current user; a bare `state === ReviewRequested`\n\t\t\t\t\t\t\t// check would also match reviews requested from OTHER people, leaking their MRs in.\n\t\t\t\t\t\t\treview =>\n\t\t\t\t\t\t\t\treview.reviewer?.username === username &&\n\t\t\t\t\t\t\t\treview.state === ProviderPullRequestReviewState.ReviewRequested,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst isAuthor = pr.author?.username === username;\n\t\t\t\t\t\t// It seems like GitLab doesn't give us mentioned PRs.\n\t\t\t\t\t\t// const isMentioned = ???;\n\n\t\t\t\t\t\treturn isAssignee || isRequestedReviewer || isAuthor;\n\t\t\t\t\t})\n\t\t\t\t\t.map(pr => fromGitLabMergeRequestProvidersApi(pr, this, 'search')),\n\t\t\t],\n\t\t\tr => r.url,\n\t\t\t(original, _current) => original,\n\t\t);\n\n\t\treturn [...results];\n\t}\n\n\tprotected override async getProviderMyPullRequestsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: { state?: PullRequestStateFilter[]; cursor?: string; filters?: PullRequestFilter[] },\n\t): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined> {\n\t\t// Resolve the username from THIS session's token (multi-account safe) to scope the account-wide read.\n\t\tconst username = (await this.getProviderCurrentAccount(session))?.username;\n\t\tif (username == null) return undefined;\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst requested = options?.filters?.length ? new Set(options.filters) : undefined;\n\t\t// The filter contract is all-or-nothing, so refuse as soon as ONE requested member is inexpressible rather\n\t\t// than only when every one is: dropping the rest would answer a narrower question than was asked\n\t\t// (`[Author, Mention]` becoming `authored`). Throw rather than return `undefined`, which the facade reads\n\t\t// as \"no session could be resolved\" and a drain turns into a not-connected warning, misattributing a\n\t\t// refusal; `getMyPullRequestsForUserResult` recovers the throw into `{ error }`. Defense in depth:\n\t\t// `resolveAccountWidePullRequestFilters` already refuses such a set, so only a direct caller of the public\n\t\t// method reaches this.\n\t\tconst unsupported = requested == null ? [] : [...requested].filter(f => gitLabAssociationForFilter[f] == null);\n\t\tif (unsupported.length > 0) {\n\t\t\tthrow new IntegrationReadUnavailableError(\n\t\t\t\tthis.name,\n\t\t\t\t`account-wide pull request filters not expressible by GitLab: ${unsupported.join(', ')}`,\n\t\t\t);\n\t\t}\n\n\t\tconst associations: GitLabPullRequestAssociation[] =\n\t\t\trequested == null\n\t\t\t\t? ['authored', 'assigned', 'reviewRequested']\n\t\t\t\t: Array.from(requested, f => gitLabAssociationForFilter[f]).filter(a => a != null);\n\n\t\tconst cursors = parseGitLabPullRequestFacetCursor(options?.cursor);\n\t\tconst resumable = associations.filter(association => cursors[association] != null);\n\t\tconst associationsToQuery = options?.cursor != null && resumable.length > 0 ? resumable : associations;\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst settled = await Promise.allSettled(\n\t\t\tassociationsToQuery.map(async association => ({\n\t\t\t\tassociation: association,\n\t\t\t\tresult: await api.getGitLabPullRequestsForUserAssociation(tokenWithInfo, username, association, {\n\t\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\t\t\tstates: toProviderPullRequestStates(options?.state),\n\t\t\t\t\tcursor: cursors[association],\n\t\t\t\t}),\n\t\t\t})),\n\t\t);\n\t\tif (settled.every(outcome => outcome.status === 'rejected')) {\n\t\t\tconst first = settled[0];\n\t\t\tif (first?.status === 'rejected') throw first.reason;\n\t\t}\n\n\t\tconst values = new Map<string, ProviderPullRequest>();\n\t\tconst nextCursors: GitLabPullRequestFacetCursor = {};\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tlet aggregateMetadata: CollectionMetadata | undefined;\n\t\tlet truncated = false;\n\t\tlet unkeyedPullRequest = 0;\n\t\tfor (let index = 0; index < settled.length; index++) {\n\t\t\tconst outcome = settled[index];\n\t\t\tconst association = associationsToQuery[index];\n\t\t\tif (outcome == null || association == null) continue;\n\t\t\tif (outcome.status === 'rejected') {\n\t\t\t\tconst failure = toCollectionScopeFailure({ providerId: this.id }, outcome.reason);\n\t\t\t\tfailures.push({\n\t\t\t\t\t...failure,\n\t\t\t\t\tmessage: `GitLab ${association} pull requests could not be read${\n\t\t\t\t\t\tfailure.message != null ? `: ${failure.message}` : ''\n\t\t\t\t\t}`,\n\t\t\t\t});\n\t\t\t\ttruncated = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst result = outcome.value.result;\n\t\t\taggregateMetadata = mergeCollectionMetadata(aggregateMetadata, result.metadata);\n\t\t\ttruncated ||=\n\t\t\t\tresult.paging?.truncated === true ||\n\t\t\t\t(result.metadata != null && result.metadata.completeness !== 'complete');\n\t\t\tfor (const pr of result.values) {\n\t\t\t\tconst identity = getProviderPullRequestIdentity(pr) ?? `unkeyed:${unkeyedPullRequest++}`;\n\t\t\t\tif (!values.has(identity)) {\n\t\t\t\t\tvalues.set(identity, pr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result.paging?.more && result.paging.cursor != null && result.paging.cursor !== '{}') {\n\t\t\t\tnextCursors[association] = result.paging.cursor;\n\t\t\t}\n\t\t}\n\n\t\taggregateMetadata = mergeCollectionMetadata(\n\t\t\taggregateMetadata,\n\t\t\tfailures.length > 0 ? { completeness: 'partial', failures: failures } : undefined,\n\t\t);\n\t\tconst hasMore = Object.keys(nextCursors).length > 0;\n\t\treturn {\n\t\t\tvalues: [...values.values()],\n\t\t\tpaging: {\n\t\t\t\tmore: hasMore,\n\t\t\t\tcursor: hasMore ? JSON.stringify({ type: 'gitlab-associations', cursors: nextCursors }) : '{}',\n\t\t\t\ttruncated: truncated || undefined,\n\t\t\t},\n\t\t\t...(aggregateMetadata != null ? { metadata: aggregateMetadata } : {}),\n\t\t};\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: GitLabRepositoryDescriptor[],\n\t): Promise<IssueShape[] | undefined> {\n\t\tconst api = await this.authenticationService.apis.gitlab;\n\t\tconst providerApi = await this.getProvidersApi();\n\t\tif (!api || !repos) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst repoIdsResult = await Promise.allSettled(\n\t\t\trepos.map((r: GitLabRepositoryDescriptor): Promise<string | undefined> =>\n\t\t\t\tapi.getProjectId(\n\t\t\t\t\tthis,\n\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\tr.owner,\n\t\t\t\t\tr.name,\n\t\t\t\t\tthis.apiBaseUrlFor(session),\n\t\t\t\t\tundefined,\n\t\t\t\t),\n\t\t\t) ?? [],\n\t\t);\n\t\tconst repoInput = repoIdsResult\n\t\t\t.map(result => (result.status === 'fulfilled' ? result.value : undefined))\n\t\t\t.filter((r): r is string => r != null);\n\t\tconst apiResult = await providerApi.getIssuesForRepos(toTokenWithInfo(this.id, session), repoInput, {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t});\n\n\t\treturn apiResult.values\n\t\t\t.map(issue => toIssueShape(issue, this, { projection: 'account' }))\n\t\t\t.filter((result): result is IssueShape => result != null);\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for GitLab. GitLab's repo-scoped issue read bails without repos, and GitLab has\n\t * no GraphQL cross-project issue field, so this goes through the SDK's REST `GET /issues` read\n\t * ({@link ProvidersApi.getIssuesForCurrentUser}). Open issues only (matching GitHub's baked `is:open`).\n\t *\n\t * Default scope is the current user's assigned issues; `includeAllAssignees` broadens to `scope=all` (every\n\t * visible issue, any assignee). The read is numbered-paged, so each page is drained to exhaustion, bounded by\n\t * a defensive backstop — a hit backstop is reported as `truncated` so the facade surfaces an incomplete read\n\t * rather than publishing a partial list as complete.\n\t *\n\t * `options.filters` selects which relationships to read: `Assignee` (the default when omitted) and `Author`,\n\t * each its own drain, unioned by url — see the comment on `passes` for why they can't be one request. Mention\n\t * is absent because GitLab's REST read exposes no first-class mention filter, and approximating it via `search`\n\t * would return a different set than asked for; the facade refuses it\n\t * (see `ProviderMetadata.supportedAccountWideIssueFilters`).\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources?: GitLabRepositoryDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst api = await this.getProvidersApi();\n\n\t\t// Resolve the username from THIS session's token (multi-account safe), matching the PR account-wide read.\n\t\t// Only needed to scope the default (assigned-to-me) read; the all-assignees broaden drops it.\n\t\tconst username = options?.includeAllAssignees\n\t\t\t? undefined\n\t\t\t: (await this.getProviderCurrentAccount(session))?.username;\n\t\tif (!options?.includeAllAssignees && username == null) return undefined;\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst baseUrl = this.enterpriseBaseUrlFor(session);\n\t\tconst maxPages = 20;\n\t\t// Dedupe by `url`, not `IssueShape.id`: for GitLab `id` is the per-project `iid`, which collides across\n\t\t// projects in an account-wide read (two repos both have issue `#1`), so an id-keyed map would silently\n\t\t// drop distinct issues. `url` is globally unique. Matches the GitHub/GitLab account-wide PR reads.\n\t\tconst issuesByUrl = new Map<string, IssueShape>();\n\t\tlet truncated = false;\n\t\tlet collectionMetadata: CollectionMetadata | undefined;\n\n\t\t// One drain PER requested relationship, unioned by url. The account-wide filter contract is a union\n\t\t// (`authored ∪ assigned`, matching GitHub's three searches and Azure's two drains), and GitLab can express\n\t\t// only one relationship per REST call: `assignee_username` and `author_username` on the same request\n\t\t// compose with AND, so a single combined call would return the intersection — issues the user both opened\n\t\t// and is assigned to — instead of either set. Overlap between the passes is collapsed by the url map.\n\t\tconst passes: { scope: 'assigned_to_me' | 'all'; assigneeUsername?: string; authorUsername?: string }[] = [];\n\t\tif (options?.includeAllAssignees) {\n\t\t\t// Broadens past \"mine\" entirely: every visible issue, any assignee. Contradicts `filters`, which the\n\t\t\t// facade refuses before reaching here.\n\t\t\tpasses.push({ scope: 'all' });\n\t\t} else {\n\t\t\tconst filters = options?.filters;\n\t\t\t// Assignee is this read's default relationship, so it runs unless the caller narrowed to author alone.\n\t\t\tif (filters == null || filters.length === 0 || filters.includes(IssueFilter.Assignee)) {\n\t\t\t\tpasses.push({ scope: 'assigned_to_me', assigneeUsername: username });\n\t\t\t}\n\t\t\t// `scope: 'all'` is required for the author axis: paired with `assigned_to_me` GitLab would intersect\n\t\t\t// the two rather than read authored issues.\n\t\t\tif (filters?.includes(IssueFilter.Author)) {\n\t\t\t\tpasses.push({ scope: 'all', authorUsername: username });\n\t\t\t}\n\t\t}\n\n\t\tfor (const pass of passes) {\n\t\t\tconst result = await collectProviderPagedResult(\n\t\t\t\tasync cursor => {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\tconst page = await api.getIssuesForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\t\t\t\t...pass,\n\t\t\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\t\t\tbaseUrl: baseUrl,\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tsort: options?.sort,\n\t\t\t\t\t});\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\t\t\t\treturn page;\n\t\t\t\t},\n\t\t\t\tmaxPages,\n\t\t\t\t{ providerId: this.id },\n\t\t\t);\n\n\t\t\tfor (const issue of result.values) {\n\t\t\t\tconst shape = toIssueShape(issue, this, { projection: 'account' });\n\t\t\t\tif (shape != null && !issuesByUrl.has(shape.url)) {\n\t\t\t\t\tissuesByUrl.set(shape.url, shape);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttruncated ||= result.truncated === true;\n\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, result.metadata);\n\t\t}\n\n\t\treturn {\n\t\t\tvalues: [...issuesByUrl.values()],\n\t\t\ttruncated: truncated,\n\t\t\t...(collectionMetadata != null ? { metadata: collectionMetadata } : {}),\n\t\t};\n\t}\n\n\tprotected override async searchProviderPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\tsearchQuery: string,\n\t\trepos?: GitLabRepositoryDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tconst api = await this.authenticationService.apis.gitlab;\n\t\tif (!api) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn api.searchPullRequests(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t{\n\t\t\t\tsearch: searchQuery,\n\t\t\t\trepos: repos?.map(r => `${r.owner}/${r.name}`),\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t\tinclude: options?.include,\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async mergeProviderPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tpr: PullRequest,\n\t\toptions?: {\n\t\t\tmergeMethod?: PullRequestMergeMethod;\n\t\t},\n\t): Promise<boolean> {\n\t\tif (!this.isPullRequest(pr)) return false;\n\n\t\tconst api = await this.getProvidersApi();\n\t\ttry {\n\t\t\tconst res = await api.mergePullRequest(toTokenWithInfo(this.id, session), pr, {\n\t\t\t\t...options,\n\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\t});\n\t\t\treturn res;\n\t\t} catch (ex) {\n\t\t\tvoid this.showMergeErrorMessage(ex);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate async showMergeErrorMessage(ex: Error) {\n\t\t// Unfortunately, providers-api does not let us know the exact reason for the error,\n\t\t// so we show the same message to everything.\n\t\t// When we update the library, we can improve the error handling here.\n\t\tconst reauthenticate = await this.ctx.hooks?.onReauthenticationRequired?.(\n\t\t\tl10n.t(\n\t\t\t\t\"{0}. Would you like to try reauthenticating to provide additional access? Your token needs to have the 'api' scope to perform merge.\",\n\t\t\t\tex.message,\n\t\t\t),\n\t\t);\n\n\t\tif (reauthenticate) {\n\t\t\tawait this.reauthenticate();\n\t\t}\n\t}\n\n\tprivate isPullRequest(pr: PullRequest | { id: string; headRefSha: string }): pr is PullRequest {\n\t\treturn (pr as PullRequest).refs != null;\n\t}\n\n\tprotected override async getProviderCurrentAccount(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst currentUser = await api.getCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t});\n\t\tif (currentUser == null) return undefined;\n\n\t\treturn {\n\t\t\tprovider: {\n\t\t\t\tid: this.id,\n\t\t\t\tname: this.name,\n\t\t\t\tdomain: this.domain,\n\t\t\t\ticon: this.icon,\n\t\t\t},\n\t\t\tid: currentUser.id,\n\t\t\tname: currentUser.name || undefined,\n\t\t\temail: currentUser.email || undefined,\n\t\t\tavatarUrl: currentUser.avatarUrl || undefined,\n\t\t\tusername: currentUser.username || undefined,\n\t\t};\n\t}\n\n\tprotected override getProviderPullRequestIdentityFromMaybeUrl(search: string): PullRequestUrlIdentity | undefined {\n\t\treturn getGitLabPullRequestIdentityFromMaybeUrl(search, this.id);\n\t}\n}\n\nexport class GitLabIntegration extends GitLabIntegrationBase<GitCloudHostIntegrationId.GitLab> {\n\treadonly authProvider = authProvider;\n\treadonly id = GitCloudHostIntegrationId.GitLab;\n\tprotected readonly key = this.id;\n\treadonly name: string = 'GitLab';\n\tget domain(): string {\n\t\treturn metadata.domain;\n\t}\n\n\tprotected apiBaseUrlFor(_session: ProviderAuthenticationSession): string {\n\t\treturn 'https://gitlab.com/api';\n\t}\n\n\toverride access(): Promise<boolean> {\n\t\t// Always allow GitHub cloud integration access\n\t\treturn Promise.resolve(true);\n\t}\n}\n\nexport class GitLabSelfHostedIntegration extends GitLabIntegrationBase<GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted> {\n\treadonly authProvider = cloudEnterpriseAuthProvider;\n\treadonly id = GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted;\n\tprotected readonly key;\n\treadonly name = 'GitLab Self-Hosted';\n\tget domain(): string {\n\t\treturn this._domain;\n\t}\n\tprotected override apiBaseUrlFor(session: ProviderAuthenticationSession): string {\n\t\treturn `${this.getSelfManagedApiBaseUrl(session)}/api`;\n\t}\n\n\tconstructor(\n\t\tctx: IntegrationServiceContext,\n\t\tauthenticationService: IntegrationAuthenticationService,\n\t\tgetProvidersApi: () => Promise<ProvidersApi>,\n\t\tdidChangeConnection: Emitter<IntegrationConnectionChangeEvent>,\n\t\tprivate readonly _domain: string,\n\t) {\n\t\tsuper(ctx, authenticationService, getProvidersApi, didChangeConnection);\n\t\tthis.key = `${this.id}:${this.domain}` as const;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gitlab.js","names":[],"sources":["../../../../../plus/integrations/src/providers/gitlab.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScopeFailure } from '@gitkraken/provider-apis';\nimport * as l10n from '@vscode/l10n';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type {\n\tPullRequest,\n\tPullRequestMergeMethod,\n\tPullRequestShape,\n\tPullRequestState,\n\tPullRequestStateFilter,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { RepositoryDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport type { PullRequestUrlIdentity } from '@gitlens/git/utils/pullRequest.utils.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport type { Emitter } from '@gitlens/utils/event.js';\nimport { uniqueBy } from '@gitlens/utils/iterable.js';\nimport { batch, mapSettledBounded } from '@gitlens/utils/promise.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport { IntegrationReadUnavailableError } from '../errors.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport type { SearchMyPullRequestsOptions, SearchPullRequestsOptions } from '../models/gitHostIntegration.js';\nimport { GitHostIntegration } from '../models/gitHostIntegration.js';\nimport type {\n\tAccountWideIssuesResult,\n\tIssueEtagFields,\n\tIssueEtagInclude,\n\tPullRequestEtagFields,\n\tPullRequestEtagInclude,\n\tSearchMyIssuesOptions,\n} from '../models/integration.js';\nimport type { GitLabIntegrationIds } from './gitlab/gitlab.utils.js';\nimport { getGitLabPullRequestIdentityFromMaybeUrl, matchesGitLabOrgNamespace } from './gitlab/gitlab.utils.js';\nimport {\n\tfromGitLabMergeRequestProvidersApi,\n\tgitLabEtagFieldsMaxIids,\n\tgitLabEtagFieldsWithChecksMaxIids,\n\ttoGitLabIssueEtagFields,\n\ttoGitLabPullRequestEtagFields,\n} from './gitlab/models.js';\nimport type {\n\tProviderApiPagedResult,\n\tProviderHierarchyResult,\n\tProviderOrganization,\n\tProviderPullRequest,\n\tProviderRepository,\n} from './models.js';\nimport {\n\tfromProviderPullRequest,\n\tgetProviderPullRequestIdentity,\n\tIssueFilter,\n\tProviderPullRequestReviewState,\n\tprovidersMetadata,\n\tPullRequestFilter,\n\ttoIssueShape,\n\ttoProviderPullRequestStates,\n} from './models.js';\nimport type { ProvidersApi } from './providersApi.js';\nimport {\n\tcollectProviderPagedResult,\n\tmergeCollectionMetadata,\n\treadEtagFieldsByRepository,\n\tresolveBranchPullRequests,\n} from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[GitCloudHostIntegrationId.GitLab];\nconst authProvider: IntegrationAuthenticationProviderDescriptor = Object.freeze({\n\tid: metadata.id,\n\tscopes: metadata.scopes,\n});\n\nconst cloudEnterpriseMetadata = providersMetadata[GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted];\nconst cloudEnterpriseAuthProvider: IntegrationAuthenticationProviderDescriptor = Object.freeze({\n\tid: cloudEnterpriseMetadata.id,\n\tscopes: cloudEnterpriseMetadata.scopes,\n});\n\ntype GitLabPullRequestAssociation = 'assigned' | 'authored' | 'reviewRequested';\ntype GitLabPullRequestFacetCursor = Partial<Record<GitLabPullRequestAssociation, string>>;\n\n/**\n * The account-wide relationships GitLab can express, and the `scope` each maps to. `undefined` means GitLab has\n * no equivalent axis (it has neither `Mention` nor `reviewed-by`), and this map is the single declaration of\n * that. Exhaustive over `PullRequestFilter` on purpose: a new member must be decided for GitLab here rather\n * than compiling through as silently unsupported. Exported for the advertised-vs-expressible capability test,\n * which is what keeps it agreeing with `supportedAccountWidePullRequestFilters`; no runtime consumer imports it.\n */\nexport const gitLabAssociationForFilter: Record<PullRequestFilter, GitLabPullRequestAssociation | undefined> = {\n\t[PullRequestFilter.Assignee]: 'assigned',\n\t[PullRequestFilter.Author]: 'authored',\n\t[PullRequestFilter.ReviewRequested]: 'reviewRequested',\n\t[PullRequestFilter.Reviewed]: undefined,\n\t[PullRequestFilter.Mention]: undefined,\n};\n\nfunction parseGitLabPullRequestFacetCursor(cursor: string | undefined): GitLabPullRequestFacetCursor {\n\tif (!cursor) return {};\n\n\ttry {\n\t\tconst parsed = JSON.parse(cursor) as { type?: unknown; cursors?: unknown };\n\t\tif (parsed.type !== 'gitlab-associations' || parsed.cursors == null || typeof parsed.cursors !== 'object') {\n\t\t\treturn {};\n\t\t}\n\n\t\tconst cursors = parsed.cursors as Record<string, unknown>;\n\t\treturn Object.fromEntries(\n\t\t\t(['assigned', 'authored', 'reviewRequested'] as const)\n\t\t\t\t.filter(association => typeof cursors[association] === 'string' && cursors[association] !== '')\n\t\t\t\t.map(association => [association, cursors[association] as string]),\n\t\t);\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nexport type GitLabRepositoryDescriptor = RepositoryDescriptor;\n\n/** How many SSH signing-key lookups (account resolve + keys fetch) to run concurrently, to avoid a request burst. */\nconst sshSigningKeyResolveBatchSize = 10;\n\nabstract class GitLabIntegrationBase<ID extends GitLabIntegrationIds> extends GitHostIntegration<\n\tID,\n\tGitLabRepositoryDescriptor\n> {\n\tprotected abstract apiBaseUrlFor(session: ProviderAuthenticationSession): string;\n\n\t/** The installation base the PAT-based reads take on self-hosted GitLab; gitlab.com passes none. */\n\tprotected enterpriseBaseUrlFor(session: ProviderAuthenticationSession): string | undefined {\n\t\treturn this.isEnterprise ? this.getSelfManagedApiBaseUrl(session) : undefined;\n\t}\n\n\t/** Self-hosted GitLab uses PAT semantics and a domain-based API base; gitlab.com does not. */\n\tprotected get isEnterprise(): boolean {\n\t\treturn this.id === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted;\n\t}\n\n\tprotected override async getProviderAccountForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getAccountForCommit(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\trev,\n\t\t\t{\n\t\t\t\t...options,\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderAccountForEmail(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\temail: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getAccountForEmail(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\temail,\n\t\t\t{\n\t\t\t\t...options,\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderSshSigningKeysForEmails(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\temails: string[],\n\t): Promise<Map<string, string[]>> {\n\t\tconst result = new Map<string, string[]>();\n\n\t\tconst api = await this.authenticationService.apis.gitlab;\n\t\tif (api == null) return result;\n\n\t\tconst token = toTokenWithInfo(this.id, session);\n\n\t\t// GitLab resolves users one email at a time (no batch search). Run them in bounded batches rather than firing all\n\t\t// (up to providerVerifyLimit) account + key lookups at once, to avoid a request burst that trips rate limiting.\n\t\tawait batch(emails, sshSigningKeyResolveBatchSize, async email => {\n\t\t\tconst account = await this.getProviderAccountForEmail(session, repo, email);\n\t\t\tif (account?.id == null) return;\n\n\t\t\tconst keys = await api.getUserSigningKeys(this, token, account.id, {\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t});\n\t\t\tresult.set(\n\t\t\t\temail.toLowerCase(),\n\t\t\t\tkeys.map(k => k.key),\n\t\t\t);\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tprotected override async getProviderDefaultBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t): Promise<DefaultBranch | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getDefaultBranch(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\t{ id }: { id: string; key: string },\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getIssueOrPullRequest(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tNumber(id),\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.authenticationService.apis.gitlab;\n\t\tconst providerApi = await this.getProvidersApi();\n\t\tif (!api || !repo || !id) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst repoId = await api.getProjectId(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tthis.apiBaseUrlFor(session),\n\t\t\tundefined,\n\t\t);\n\t\tif (!repoId) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst apiResult = await providerApi.getIssue(\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t{ namespace: repo.owner, name: repo.name, number: id },\n\t\t\t{\n\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t\tconst issue = apiResult != null ? toIssueShape(apiResult, this, { projection: 'point' }) : undefined;\n\t\treturn issue != null ? { ...issue, type: 'issue' } : undefined;\n\t}\n\n\tprotected override async getProviderPullRequestForBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\tbranch: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t\tinclude?: PullRequestState[];\n\t\t},\n\t): Promise<PullRequest | undefined> {\n\t\tconst { include, ...opts } = options ?? {};\n\n\t\tconst toGitLabMergeRequestState = (await import(/* webpackChunkName: \"integrations\" */ './gitlab/models.js'))\n\t\t\t.toGitLabMergeRequestState;\n\t\treturn (await this.authenticationService.apis.gitlab)?.getPullRequestForBranch(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tbranch,\n\t\t\t{\n\t\t\t\t...opts,\n\t\t\t\tinclude: include?.map(s => toGitLabMergeRequestState(s)),\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\trev: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getPullRequestForCommit(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\trev,\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: GitLabRepositoryDescriptor,\n\t\tid: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getPullRequest(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tresource.owner,\n\t\t\tresource.name,\n\t\t\tparseInt(id, 10),\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * One request per target, settled independently so one target's failure rejects only its own slot; through the\n\t * SDK's issue read and the conversion the repo-scoped list rows take. GitLens' own GitLab client can't alias\n\t * the read instead: it fails a whole document on any GraphQL error, where a batch needs per-target errors.\n\t */\n\tprotected override async getProviderIssuesBatch(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcoordinates: readonly { owner: string; repo: string; number: number; project?: string }[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<IssueShape | undefined>[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst baseUrl = this.enterpriseBaseUrlFor(session);\n\t\t// The confirming read must ask the same host, over the same protocol, as the read it confirms.\n\t\tconst confirmBaseUrl = this.apiBaseUrlFor(session);\n\n\t\treturn mapSettledBounded(coordinates, providerFanOutConcurrency, async c => {\n\t\t\tconst issue = await api.getIssueForRepo(tokenWithInfo, { namespace: c.owner, name: c.repo }, c.number, {\n\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\tbaseUrl: baseUrl,\n\t\t\t});\n\t\t\tif (issue != null) {\n\t\t\t\tconst shape = toIssueShape(issue, this, { projection: 'batch' });\n\t\t\t\tif (shape == null) throw new Error(`GitLab returned issue ${c.number} without a URL or update time`);\n\n\t\t\t\treturn shape;\n\t\t\t}\n\n\t\t\t// provider-apis' \"not found\" can come from a reply carrying only GraphQL errors (see `getIssueForRepo`),\n\t\t\t// so only our own strict read can prove the miss.\n\t\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\t\tif (gitlab == null) {\n\t\t\t\tthrow new IntegrationReadUnavailableError(this.name, 'cannot confirm a missing issue');\n\t\t\t}\n\n\t\t\tconst exists = await gitlab.hasIssue(\n\t\t\t\tthis,\n\t\t\t\ttokenWithInfo,\n\t\t\t\tc.owner,\n\t\t\t\tc.repo,\n\t\t\t\tc.number,\n\t\t\t\t{ baseUrl: confirmBaseUrl, deferFailure: true },\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\t// Our own query can't produce the list rows' shape, so a disagreement fails rather than answering thinner.\n\t\t\tif (exists) throw new Error(`GitLab reported issue ${c.number} missing, then found it`);\n\n\t\t\treturn undefined;\n\t\t});\n\t}\n\n\t/**\n\t * One request per target, settled independently so one target's failure rejects only its own slot; through\n\t * the same SDK read and conversion the manager's list rows take.\n\t */\n\tprotected override async getProviderPullRequestsBatch(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcoordinates: readonly { owner: string; repo: string; number: number; project?: string }[],\n\t\toptions: { currentAccount?: { id: string; username?: string } } | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<PullRequestShape | undefined>[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst baseUrl = this.enterpriseBaseUrlFor(session);\n\t\t// The confirming read must ask the same host, over the same protocol, as the read it confirms.\n\t\tconst confirmBaseUrl = this.apiBaseUrlFor(session);\n\n\t\treturn mapSettledBounded(coordinates, providerFanOutConcurrency, async c => {\n\t\t\tconst pr = await api.getPullRequestForRepo(tokenWithInfo, { namespace: c.owner, name: c.repo }, c.number, {\n\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\tbaseUrl: baseUrl,\n\t\t\t});\n\t\t\tif (pr != null) {\n\t\t\t\treturn fromProviderPullRequest(pr, this, {\n\t\t\t\t\tcurrentAccount: options?.currentAccount,\n\t\t\t\t\tprojection: 'batch',\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// provider-apis' GitLab GraphQL helper ignores the response's `errors`, so a reply carrying only errors\n\t\t\t// (e.g. a server-side timeout) comes back as `null`, the same as not-found. Our own client throws on\n\t\t\t// `errors`, and `strict` makes it answer `undefined` only for a real miss.\n\t\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\t\tif (gitlab == null) {\n\t\t\t\tthrow new IntegrationReadUnavailableError(this.name, 'cannot confirm a missing merge request');\n\t\t\t}\n\n\t\t\tconst confirmed = await gitlab.getPullRequest(\n\t\t\t\tthis,\n\t\t\t\ttokenWithInfo,\n\t\t\t\tc.owner,\n\t\t\t\tc.repo,\n\t\t\t\tc.number,\n\t\t\t\t{ baseUrl: confirmBaseUrl, strict: true, deferFailure: true },\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\t// Our own client's row can't produce the list rows' shape (`id` is the iid, not the global id; no\n\t\t\t// `authoredByMe`), so a disagreement fails rather than answering with a different identity.\n\t\t\tif (confirmed != null) throw new Error(`GitLab reported pull request ${c.number} missing, then found it`);\n\n\t\t\treturn undefined;\n\t\t});\n\t}\n\n\t/**\n\t * The cheap check behind the batch issue read's etags: GitLens' own GitLab client reads each project's issues by\n\t * iid, up to {@link gitLabEtagFieldsMaxIids} in ONE request, where {@link getProviderIssuesBatch} sends one or\n\t * two per target. Each row is converted as provider-apis and `toIssueShape` convert the full one, so both reads\n\t * compute the same etag, and `'reactions'` adds the upvote count the full row's `thumbsUpCount` comes from. A\n\t * request that throws rejects only its own targets' slots.\n\t */\n\tprotected override async getProviderIssuesEtagFields(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcoordinates: readonly { owner: string; repo: string; number: number; project?: string }[],\n\t\toptions: { etagIncludes?: readonly IssueEtagInclude[] },\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined> {\n\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\tif (gitlab == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t// The host and protocol the full read's confirming read asks.\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\t\tconst etagIncludes = options.etagIncludes ?? [];\n\n\t\treturn readEtagFieldsByRepository(\n\t\t\tcoordinates,\n\t\t\tgitLabEtagFieldsMaxIids,\n\t\t\t(owner, repo, iids) =>\n\t\t\t\tgitlab.getIssuesEtagFields(\n\t\t\t\t\tthis,\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t`${owner}/${repo}`,\n\t\t\t\t\tiids,\n\t\t\t\t\t{ baseUrl: baseUrl, etagIncludes: etagIncludes, deferFailure: true },\n\t\t\t\t\tcancellation,\n\t\t\t\t),\n\t\t\tnode => toGitLabIssueEtagFields(node, etagIncludes),\n\t\t);\n\t}\n\n\t/**\n\t * The cheap check behind the batch pull request read's etags: {@link getProviderIssuesEtagFields}'s requests and\n\t * failure isolation over merge requests, selecting a merge request's change state plus the fields of each of\n\t * `options.etagIncludes`. Every include is meaningful here: a full row carries a mergeability, a review decision\n\t * and a check rollup, each of which can change without moving `updatedAt`.\n\t */\n\tprotected override async getProviderPullRequestsEtagFields(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcoordinates: readonly { owner: string; repo: string; number: number; project?: string }[],\n\t\toptions: { etagIncludes?: readonly PullRequestEtagInclude[] },\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<PullRequestEtagFields | undefined>[] | undefined> {\n\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\tif (gitlab == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t// The host and protocol the full read's confirming read asks.\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\t\tconst etagIncludes = options.etagIncludes ?? [];\n\n\t\treturn readEtagFieldsByRepository(\n\t\t\tcoordinates,\n\t\t\tetagIncludes.includes('checks') ? gitLabEtagFieldsWithChecksMaxIids : gitLabEtagFieldsMaxIids,\n\t\t\t(owner, repo, iids) =>\n\t\t\t\tgitlab.getMergeRequestsEtagFields(\n\t\t\t\t\tthis,\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t`${owner}/${repo}`,\n\t\t\t\t\tiids,\n\t\t\t\t\t{ baseUrl: baseUrl, etagIncludes: etagIncludes, deferFailure: true },\n\t\t\t\t\tcancellation,\n\t\t\t\t),\n\t\t\tnode => toGitLabPullRequestEtagFields(node, etagIncludes),\n\t\t);\n\t}\n\n\t/**\n\t * Two steps, because provider-apis has no source-branch filter: GitLens' own GitLab client finds each branch's\n\t * matching iids, one request per target, then {@link getProviderPullRequestsBatch} resolves them — so a row is\n\t * the one `getPullRequestsBatch` returns for that merge request, `url` and `authoredByMe` included.\n\t */\n\tprotected override async getProviderPullRequestsForBranches(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly { owner: string; repo: string; project?: string; branch: string; headOwner?: string }[],\n\t\toptions: { currentAccount?: { id: string; username?: string }; limit: number },\n\t\tcancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<{ pullRequests: PullRequestShape[]; truncated: boolean }>[] | undefined> {\n\t\tconst gitlab = await this.authenticationService.apis.gitlab;\n\t\tif (gitlab == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t// The session's own host and protocol, as the batch read's confirming read uses.\n\t\tconst apiBaseUrl = this.apiBaseUrlFor(session);\n\n\t\tconst found = await mapSettledBounded(targets, providerFanOutConcurrency, t =>\n\t\t\tgitlab.getPullRequestNumbersForBranch(\n\t\t\t\tthis,\n\t\t\t\ttokenWithInfo,\n\t\t\t\tt.owner,\n\t\t\t\tt.repo,\n\t\t\t\tt.branch,\n\t\t\t\t{ baseUrl: apiBaseUrl, headOwner: t.headOwner, limit: options.limit, deferFailure: true },\n\t\t\t\tcancellation,\n\t\t\t),\n\t\t);\n\t\treturn resolveBranchPullRequests(targets, found, coordinates =>\n\t\t\tthis.getProviderPullRequestsBatch(\n\t\t\t\tsession,\n\t\t\t\tcoordinates,\n\t\t\t\t{ currentAccount: options.currentAccount },\n\t\t\t\tcancellation,\n\t\t\t),\n\t\t);\n\t}\n\n\tpublic override async getRepoInfo(repo: {\n\t\towner: string;\n\t\tname: string;\n\t\tproject?: string;\n\t\tconnectionId?: string;\n\t}): Promise<ProviderRepository | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSessionOrThrow(repo.connectionId, undefined);\n\t\tif (session == null) return undefined;\n\n\t\treturn api.getRepo(toTokenWithInfo(this.id, session), repo.owner, repo.name, repo.project, {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t});\n\t}\n\n\tprotected override async getProviderRepositoryMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: GitLabRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\treturn (await this.authenticationService.apis.gitlab)?.getRepositoryMetadata(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async getProviderOrganizationsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst result = await api.getGitlabGroupsForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t});\n\t\treturn {\n\t\t\tvalues: result.values.map(g => ({ id: g.id, providerId: this.id, name: g.fullPath, url: g.webUrl })),\n\t\t\t...(result.truncated ? { truncated: true } : {}),\n\t\t\t...(result.metadata != null ? { metadata: result.metadata } : {}),\n\t\t};\n\t}\n\n\t/**\n\t * GitLab has no group-scoped repos endpoint, so each page is a page of the current user's member repos\n\t * across all namespaces, filtered to `org` and its subgroups (mirroring gkcli's `gitlabOrgMatches`).\n\t * Follow `paging.cursor` to page the full list; repos the user isn't a member of aren't returned.\n\t * Because the filter is applied per page, a page can come back with `values: []` while\n\t * `paging.more` is still `true` (e.g. a page full of repos outside `org`) — don't treat an empty\n\t * page as the end of pagination, only `paging.more`/`collectPagedResults` reaching exhaustion.\n\t */\n\tprotected override async getProviderRepositoriesForOrg(\n\t\tsession: ProviderAuthenticationSession,\n\t\torg: string,\n\t\toptions?: { cursor?: string },\n\t): Promise<ProviderHierarchyResult<ProviderRepository> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst result = await api.getReposForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\tcursor: options?.cursor,\n\t\t});\n\t\treturn {\n\t\t\tvalues: result.values.filter(r => matchesGitLabOrgNamespace(r.namespace, org)),\n\t\t\t...(result.paging != null ? { paging: result.paging } : {}),\n\t\t};\n\t}\n\n\tprotected override async getProviderRepositoriesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: { cursor?: string },\n\t): Promise<ProviderHierarchyResult<ProviderRepository> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// GitLab's membership read is already user-affiliated, so the account-wide walk is the unfiltered\n\t\t// version of the per-org read above (which pages the same source and filters by namespace).\n\t\treturn api.getReposForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\tcursor: options?.cursor,\n\t\t});\n\t}\n\n\tprotected override async searchProviderMyPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: GitLabRepositoryDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t\toptions?: SearchMyPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// Resolve the username from THIS session's token (multi-account: `session` may be a non-primary\n\t\t// connection); `getCurrentAccount()` would use the primary `_session` and filter by the wrong user.\n\t\tconst username = (await this.getProviderCurrentAccount(session))?.username;\n\t\tif (!username) {\n\t\t\treturn Promise.resolve([]);\n\t\t}\n\n\t\tconst apiResult = await api.getPullRequestsForUser(toTokenWithInfo(this.id, session), username, {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\tstates: toProviderPullRequestStates(options?.state),\n\t\t});\n\n\t\tif (apiResult == null) {\n\t\t\treturn Promise.resolve([]);\n\t\t}\n\n\t\t// now I'm going to filter prs from the result according to the repos parameter\n\t\tlet prs;\n\t\tif (repos != null) {\n\t\t\tconst repoMap = new Map<string, GitLabRepositoryDescriptor>();\n\t\t\tfor (const repo of repos) {\n\t\t\t\trepoMap.set(`${repo.owner}/${repo.name}`, repo);\n\t\t\t}\n\t\t\tprs = apiResult.values.filter(pr => {\n\t\t\t\tconst repo = repoMap.get(`${pr.repository.owner.login}/${pr.repository.name}`);\n\t\t\t\treturn repo != null;\n\t\t\t});\n\t\t} else {\n\t\t\tprs = apiResult.values;\n\t\t}\n\n\t\tconst results: IterableIterator<PullRequest> = uniqueBy(\n\t\t\t[\n\t\t\t\t...prs\n\t\t\t\t\t.filter(pr => {\n\t\t\t\t\t\tconst isAssignee = pr.assignees?.some(a => a.username === username);\n\t\t\t\t\t\tconst isRequestedReviewer = pr.reviews?.some(\n\t\t\t\t\t\t\t// Match only reviews assigned to the current user; a bare `state === ReviewRequested`\n\t\t\t\t\t\t\t// check would also match reviews requested from OTHER people, leaking their MRs in.\n\t\t\t\t\t\t\treview =>\n\t\t\t\t\t\t\t\treview.reviewer?.username === username &&\n\t\t\t\t\t\t\t\treview.state === ProviderPullRequestReviewState.ReviewRequested,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst isAuthor = pr.author?.username === username;\n\t\t\t\t\t\t// It seems like GitLab doesn't give us mentioned PRs.\n\t\t\t\t\t\t// const isMentioned = ???;\n\n\t\t\t\t\t\treturn isAssignee || isRequestedReviewer || isAuthor;\n\t\t\t\t\t})\n\t\t\t\t\t.map(pr => fromGitLabMergeRequestProvidersApi(pr, this, 'search')),\n\t\t\t],\n\t\t\tr => r.url,\n\t\t\t(original, _current) => original,\n\t\t);\n\n\t\treturn [...results];\n\t}\n\n\tprotected override async getProviderMyPullRequestsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: { state?: PullRequestStateFilter[]; cursor?: string; filters?: PullRequestFilter[] },\n\t): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined> {\n\t\t// Resolve the username from THIS session's token (multi-account safe) to scope the account-wide read.\n\t\tconst username = (await this.getProviderCurrentAccount(session))?.username;\n\t\tif (username == null) return undefined;\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst requested = options?.filters?.length ? new Set(options.filters) : undefined;\n\t\t// The filter contract is all-or-nothing, so refuse as soon as ONE requested member is inexpressible rather\n\t\t// than only when every one is: dropping the rest would answer a narrower question than was asked\n\t\t// (`[Author, Mention]` becoming `authored`). Throw rather than return `undefined`, which the facade reads\n\t\t// as \"no session could be resolved\" and a drain turns into a not-connected warning, misattributing a\n\t\t// refusal; `getMyPullRequestsForUserResult` recovers the throw into `{ error }`. Defense in depth:\n\t\t// `resolveAccountWidePullRequestFilters` already refuses such a set, so only a direct caller of the public\n\t\t// method reaches this.\n\t\tconst unsupported = requested == null ? [] : [...requested].filter(f => gitLabAssociationForFilter[f] == null);\n\t\tif (unsupported.length > 0) {\n\t\t\tthrow new IntegrationReadUnavailableError(\n\t\t\t\tthis.name,\n\t\t\t\t`account-wide pull request filters not expressible by GitLab: ${unsupported.join(', ')}`,\n\t\t\t);\n\t\t}\n\n\t\tconst associations: GitLabPullRequestAssociation[] =\n\t\t\trequested == null\n\t\t\t\t? ['authored', 'assigned', 'reviewRequested']\n\t\t\t\t: Array.from(requested, f => gitLabAssociationForFilter[f]).filter(a => a != null);\n\n\t\tconst cursors = parseGitLabPullRequestFacetCursor(options?.cursor);\n\t\tconst resumable = associations.filter(association => cursors[association] != null);\n\t\tconst associationsToQuery = options?.cursor != null && resumable.length > 0 ? resumable : associations;\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst settled = await Promise.allSettled(\n\t\t\tassociationsToQuery.map(async association => ({\n\t\t\t\tassociation: association,\n\t\t\t\tresult: await api.getGitLabPullRequestsForUserAssociation(tokenWithInfo, username, association, {\n\t\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\t\t\tstates: toProviderPullRequestStates(options?.state),\n\t\t\t\t\tcursor: cursors[association],\n\t\t\t\t}),\n\t\t\t})),\n\t\t);\n\t\tif (settled.every(outcome => outcome.status === 'rejected')) {\n\t\t\tconst first = settled[0];\n\t\t\tif (first?.status === 'rejected') throw first.reason;\n\t\t}\n\n\t\tconst values = new Map<string, ProviderPullRequest>();\n\t\tconst nextCursors: GitLabPullRequestFacetCursor = {};\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tlet aggregateMetadata: CollectionMetadata | undefined;\n\t\tlet truncated = false;\n\t\tlet unkeyedPullRequest = 0;\n\t\tfor (let index = 0; index < settled.length; index++) {\n\t\t\tconst outcome = settled[index];\n\t\t\tconst association = associationsToQuery[index];\n\t\t\tif (outcome == null || association == null) continue;\n\t\t\tif (outcome.status === 'rejected') {\n\t\t\t\tconst failure = toCollectionScopeFailure({ providerId: this.id }, outcome.reason);\n\t\t\t\tfailures.push({\n\t\t\t\t\t...failure,\n\t\t\t\t\tmessage: `GitLab ${association} pull requests could not be read${\n\t\t\t\t\t\tfailure.message != null ? `: ${failure.message}` : ''\n\t\t\t\t\t}`,\n\t\t\t\t});\n\t\t\t\ttruncated = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst result = outcome.value.result;\n\t\t\taggregateMetadata = mergeCollectionMetadata(aggregateMetadata, result.metadata);\n\t\t\ttruncated ||=\n\t\t\t\tresult.paging?.truncated === true ||\n\t\t\t\t(result.metadata != null && result.metadata.completeness !== 'complete');\n\t\t\tfor (const pr of result.values) {\n\t\t\t\tconst identity = getProviderPullRequestIdentity(pr) ?? `unkeyed:${unkeyedPullRequest++}`;\n\t\t\t\tif (!values.has(identity)) {\n\t\t\t\t\tvalues.set(identity, pr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result.paging?.more && result.paging.cursor != null && result.paging.cursor !== '{}') {\n\t\t\t\tnextCursors[association] = result.paging.cursor;\n\t\t\t}\n\t\t}\n\n\t\taggregateMetadata = mergeCollectionMetadata(\n\t\t\taggregateMetadata,\n\t\t\tfailures.length > 0 ? { completeness: 'partial', failures: failures } : undefined,\n\t\t);\n\t\tconst hasMore = Object.keys(nextCursors).length > 0;\n\t\treturn {\n\t\t\tvalues: [...values.values()],\n\t\t\tpaging: {\n\t\t\t\tmore: hasMore,\n\t\t\t\tcursor: hasMore ? JSON.stringify({ type: 'gitlab-associations', cursors: nextCursors }) : '{}',\n\t\t\t\ttruncated: truncated || undefined,\n\t\t\t},\n\t\t\t...(aggregateMetadata != null ? { metadata: aggregateMetadata } : {}),\n\t\t};\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: GitLabRepositoryDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\t// Without repos to scope to, this is an account-wide read (e.g. Start Work with no repository open), which\n\t\t// the repo-scoped read below can't serve. Hand off to the account-wide override, matching GitHub/Azure/Linear.\n\t\tif (repos == null || repos.length === 0) {\n\t\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, repos, cancellation))?.values;\n\t\t}\n\n\t\tconst api = await this.authenticationService.apis.gitlab;\n\t\tconst providerApi = await this.getProvidersApi();\n\t\tif (!api) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst repoIdsResult = await Promise.allSettled(\n\t\t\trepos.map((r: GitLabRepositoryDescriptor): Promise<string | undefined> =>\n\t\t\t\tapi.getProjectId(\n\t\t\t\t\tthis,\n\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\tr.owner,\n\t\t\t\t\tr.name,\n\t\t\t\t\tthis.apiBaseUrlFor(session),\n\t\t\t\t\tundefined,\n\t\t\t\t),\n\t\t\t) ?? [],\n\t\t);\n\t\tconst repoInput = repoIdsResult\n\t\t\t.map(result => (result.status === 'fulfilled' ? result.value : undefined))\n\t\t\t.filter((r): r is string => r != null);\n\t\tconst apiResult = await providerApi.getIssuesForRepos(toTokenWithInfo(this.id, session), repoInput, {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t});\n\n\t\treturn apiResult.values\n\t\t\t.map(issue => toIssueShape(issue, this, { projection: 'account' }))\n\t\t\t.filter((result): result is IssueShape => result != null);\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for GitLab. GitLab's repo-scoped issue read bails without repos, and GitLab has\n\t * no GraphQL cross-project issue field, so this goes through the SDK's REST `GET /issues` read\n\t * ({@link ProvidersApi.getIssuesForCurrentUser}). Open issues only (matching GitHub's baked `is:open`).\n\t *\n\t * Default scope is the current user's assigned issues; `includeAllAssignees` broadens to `scope=all` (every\n\t * visible issue, any assignee). The read is numbered-paged, so each page is drained to exhaustion, bounded by\n\t * a defensive backstop — a hit backstop is reported as `truncated` so the facade surfaces an incomplete read\n\t * rather than publishing a partial list as complete.\n\t *\n\t * `options.filters` selects which relationships to read: `Assignee` (the default when omitted) and `Author`,\n\t * each its own drain, unioned by url — see the comment on `passes` for why they can't be one request. Mention\n\t * is absent because GitLab's REST read exposes no first-class mention filter, and approximating it via `search`\n\t * would return a different set than asked for; the facade refuses it\n\t * (see `ProviderMetadata.supportedAccountWideIssueFilters`).\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources?: GitLabRepositoryDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst api = await this.getProvidersApi();\n\n\t\t// Resolve the username from THIS session's token (multi-account safe), matching the PR account-wide read.\n\t\t// Only needed to scope the default (assigned-to-me) read; the all-assignees broaden drops it.\n\t\tconst username = options?.includeAllAssignees\n\t\t\t? undefined\n\t\t\t: (await this.getProviderCurrentAccount(session))?.username;\n\t\tif (!options?.includeAllAssignees && username == null) return undefined;\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst baseUrl = this.enterpriseBaseUrlFor(session);\n\t\tconst maxPages = 20;\n\t\t// Dedupe by `url`, not `IssueShape.id`: for GitLab `id` is the per-project `iid`, which collides across\n\t\t// projects in an account-wide read (two repos both have issue `#1`), so an id-keyed map would silently\n\t\t// drop distinct issues. `url` is globally unique. Matches the GitHub/GitLab account-wide PR reads.\n\t\tconst issuesByUrl = new Map<string, IssueShape>();\n\t\tlet truncated = false;\n\t\tlet collectionMetadata: CollectionMetadata | undefined;\n\n\t\t// One drain PER requested relationship, unioned by url. The account-wide filter contract is a union\n\t\t// (`authored ∪ assigned`, matching GitHub's three searches and Azure's two drains), and GitLab can express\n\t\t// only one relationship per REST call: `assignee_username` and `author_username` on the same request\n\t\t// compose with AND, so a single combined call would return the intersection — issues the user both opened\n\t\t// and is assigned to — instead of either set. Overlap between the passes is collapsed by the url map.\n\t\tconst passes: { scope: 'assigned_to_me' | 'all'; assigneeUsername?: string; authorUsername?: string }[] = [];\n\t\tif (options?.includeAllAssignees) {\n\t\t\t// Broadens past \"mine\" entirely: every visible issue, any assignee. Contradicts `filters`, which the\n\t\t\t// facade refuses before reaching here.\n\t\t\tpasses.push({ scope: 'all' });\n\t\t} else {\n\t\t\tconst filters = options?.filters;\n\t\t\t// Assignee is this read's default relationship, so it runs unless the caller narrowed to author alone.\n\t\t\tif (filters == null || filters.length === 0 || filters.includes(IssueFilter.Assignee)) {\n\t\t\t\tpasses.push({ scope: 'assigned_to_me', assigneeUsername: username });\n\t\t\t}\n\t\t\t// `scope: 'all'` is required for the author axis: paired with `assigned_to_me` GitLab would intersect\n\t\t\t// the two rather than read authored issues.\n\t\t\tif (filters?.includes(IssueFilter.Author)) {\n\t\t\t\tpasses.push({ scope: 'all', authorUsername: username });\n\t\t\t}\n\t\t}\n\n\t\tfor (const pass of passes) {\n\t\t\tconst result = await collectProviderPagedResult(\n\t\t\t\tasync cursor => {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\tconst page = await api.getIssuesForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\t\t\t\t...pass,\n\t\t\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\t\t\tbaseUrl: baseUrl,\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tsort: options?.sort,\n\t\t\t\t\t});\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\t\t\t\treturn page;\n\t\t\t\t},\n\t\t\t\tmaxPages,\n\t\t\t\t{ providerId: this.id },\n\t\t\t);\n\n\t\t\tfor (const issue of result.values) {\n\t\t\t\tconst shape = toIssueShape(issue, this, { projection: 'account' });\n\t\t\t\tif (shape != null && !issuesByUrl.has(shape.url)) {\n\t\t\t\t\tissuesByUrl.set(shape.url, shape);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttruncated ||= result.truncated === true;\n\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, result.metadata);\n\t\t}\n\n\t\treturn {\n\t\t\tvalues: [...issuesByUrl.values()],\n\t\t\ttruncated: truncated,\n\t\t\t...(collectionMetadata != null ? { metadata: collectionMetadata } : {}),\n\t\t};\n\t}\n\n\tprotected override async searchProviderPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\tsearchQuery: string,\n\t\trepos?: GitLabRepositoryDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tconst api = await this.authenticationService.apis.gitlab;\n\t\tif (!api) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn api.searchPullRequests(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t{\n\t\t\t\tsearch: searchQuery,\n\t\t\t\trepos: repos?.map(r => `${r.owner}/${r.name}`),\n\t\t\t\tbaseUrl: this.apiBaseUrlFor(session),\n\t\t\t\tinclude: options?.include,\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async mergeProviderPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tpr: PullRequest,\n\t\toptions?: {\n\t\t\tmergeMethod?: PullRequestMergeMethod;\n\t\t},\n\t): Promise<boolean> {\n\t\tif (!this.isPullRequest(pr)) return false;\n\n\t\tconst api = await this.getProvidersApi();\n\t\ttry {\n\t\t\tconst res = await api.mergePullRequest(toTokenWithInfo(this.id, session), pr, {\n\t\t\t\t...options,\n\t\t\t\tisPAT: this.isEnterprise,\n\t\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t\t});\n\t\t\treturn res;\n\t\t} catch (ex) {\n\t\t\tvoid this.showMergeErrorMessage(ex);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate async showMergeErrorMessage(ex: Error) {\n\t\t// Unfortunately, providers-api does not let us know the exact reason for the error,\n\t\t// so we show the same message to everything.\n\t\t// When we update the library, we can improve the error handling here.\n\t\tconst reauthenticate = await this.ctx.hooks?.onReauthenticationRequired?.(\n\t\t\tl10n.t(\n\t\t\t\t\"{0}. Would you like to try reauthenticating to provide additional access? Your token needs to have the 'api' scope to perform merge.\",\n\t\t\t\tex.message,\n\t\t\t),\n\t\t);\n\n\t\tif (reauthenticate) {\n\t\t\tawait this.reauthenticate();\n\t\t}\n\t}\n\n\tprivate isPullRequest(pr: PullRequest | { id: string; headRefSha: string }): pr is PullRequest {\n\t\treturn (pr as PullRequest).refs != null;\n\t}\n\n\tprotected override async getProviderCurrentAccount(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst currentUser = await api.getCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\tisPAT: this.isEnterprise,\n\t\t\tbaseUrl: this.enterpriseBaseUrlFor(session),\n\t\t});\n\t\tif (currentUser == null) return undefined;\n\n\t\treturn {\n\t\t\tprovider: {\n\t\t\t\tid: this.id,\n\t\t\t\tname: this.name,\n\t\t\t\tdomain: this.domain,\n\t\t\t\ticon: this.icon,\n\t\t\t},\n\t\t\tid: currentUser.id,\n\t\t\tname: currentUser.name || undefined,\n\t\t\temail: currentUser.email || undefined,\n\t\t\tavatarUrl: currentUser.avatarUrl || undefined,\n\t\t\tusername: currentUser.username || undefined,\n\t\t};\n\t}\n\n\tprotected override getProviderPullRequestIdentityFromMaybeUrl(search: string): PullRequestUrlIdentity | undefined {\n\t\treturn getGitLabPullRequestIdentityFromMaybeUrl(search, this.id);\n\t}\n}\n\nexport class GitLabIntegration extends GitLabIntegrationBase<GitCloudHostIntegrationId.GitLab> {\n\treadonly authProvider = authProvider;\n\treadonly id = GitCloudHostIntegrationId.GitLab;\n\tprotected readonly key = this.id;\n\treadonly name: string = 'GitLab';\n\tget domain(): string {\n\t\treturn metadata.domain;\n\t}\n\n\tprotected apiBaseUrlFor(_session: ProviderAuthenticationSession): string {\n\t\treturn 'https://gitlab.com/api';\n\t}\n\n\toverride access(): Promise<boolean> {\n\t\t// Always allow GitHub cloud integration access\n\t\treturn Promise.resolve(true);\n\t}\n}\n\nexport class GitLabSelfHostedIntegration extends GitLabIntegrationBase<GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted> {\n\treadonly authProvider = cloudEnterpriseAuthProvider;\n\treadonly id = GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted;\n\tprotected readonly key;\n\treadonly name = 'GitLab Self-Hosted';\n\tget domain(): string {\n\t\treturn this._domain;\n\t}\n\tprotected override apiBaseUrlFor(session: ProviderAuthenticationSession): string {\n\t\treturn `${this.getSelfManagedApiBaseUrl(session)}/api`;\n\t}\n\n\tconstructor(\n\t\tctx: IntegrationServiceContext,\n\t\tauthenticationService: IntegrationAuthenticationService,\n\t\tgetProvidersApi: () => Promise<ProvidersApi>,\n\t\tdidChangeConnection: Emitter<IntegrationConnectionChangeEvent>,\n\t\tprivate readonly _domain: string,\n\t) {\n\t\tsuper(ctx, authenticationService, getProvidersApi, didChangeConnection);\n\t\tthis.key = `${this.id}:${this.domain}` as const;\n\t}\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { AutolinkReference, DynamicAutolinkReference } from "../../../git/models
3
3
  import { IssueOrPullRequest, IssueOrPullRequestType } from "../../../git/models/issueOrPullRequest.js";
4
4
  import { Issue, IssueShape } from "../../../git/models/issue.js";
5
5
  import { Account } from "../../../git/models/author.js";
6
- import { ProviderAuthenticationSession } from "../authentication/models.js";
6
+ import { ConnectionResource, ProviderAuthenticationSession } from "../authentication/models.js";
7
7
  import { IssuesCloudHostIntegrationId } from "../constants.js";
8
8
  import { ProviderApiCollectionResult } from "./models.js";
9
9
  import { AccountWideIssuesResult, IssuesForProjectOptions, ProjectIssuesDrain, ProjectIssuesRequest, SearchMyIssuesOptions } from "../models/issueReads.js";
@@ -30,8 +30,16 @@ export declare class LinearIntegration extends IssuesIntegration<IssuesCloudHost
30
30
  /** One build per token at a time: concurrent renders share it rather than each re-reading the teams. */
31
31
  private buildAutolinks;
32
32
  private buildAutolinksCore;
33
+ /** The workspace is the token's organization; kept only as a complete, trimmed pair. */
34
+ getProviderResourceForSession(session: ProviderAuthenticationSession): Promise<ConnectionResource | undefined>;
33
35
  private _organizations;
36
+ /**
37
+ * In flight by token. The request can't be aborted (provider-apis takes no signal), so a lookup that outlives its
38
+ * caller's timeout is joined by the next one rather than piling another request on top of it.
39
+ */
40
+ private readonly _organizationRequests;
34
41
  private getOrganization;
42
+ private fetchOrganization;
35
43
  /** Expires, and is dropped on a re-sync, so a team created or left mid-session is picked up (#5907). */
36
44
  private readonly _teams;
37
45
  private getTeams;
@@ -102,21 +102,44 @@ var LinearIntegration = class extends IssuesIntegration {
102
102
  });
103
103
  return autolinks;
104
104
  }
105
+ /** The workspace is the token's organization; kept only as a complete, trimmed pair. */
106
+ async getProviderResourceForSession(session) {
107
+ const organization = await this.getOrganization(session, true);
108
+ const id = organization?.id.trim();
109
+ const name = organization?.name.trim();
110
+ return id && name ? {
111
+ id,
112
+ name
113
+ } : void 0;
114
+ }
105
115
  _organizations;
116
+ /**
117
+ * In flight by token. The request can't be aborted (provider-apis takes no signal), so a lookup that outlives its
118
+ * caller's timeout is joined by the next one rather than piling another request on top of it.
119
+ */
120
+ _organizationRequests = /* @__PURE__ */ new Map();
106
121
  async getOrganization(session, force = false) {
107
122
  const { accessToken } = session;
108
- this._organizations ??= /* @__PURE__ */ new Map();
109
- if (this._organizations.get(accessToken) == null || force) {
110
- const organization = await (await this.getProvidersApi()).getLinearOrganization(toTokenWithInfo(this.id, session));
111
- const descriptor = organization && {
112
- id: organization.id,
113
- key: organization.key,
114
- name: organization.name,
115
- url: organization.url
116
- };
117
- if (descriptor) this._organizations.set(accessToken, descriptor);
123
+ const organizations = this._organizations ??= /* @__PURE__ */ new Map();
124
+ if (organizations.get(accessToken) == null || force) {
125
+ let request = this._organizationRequests.get(accessToken);
126
+ if (request == null) {
127
+ request = this.fetchOrganization(session).finally(() => this._organizationRequests.delete(accessToken));
128
+ this._organizationRequests.set(accessToken, request);
129
+ }
130
+ const descriptor = await request;
131
+ if (descriptor) organizations.set(accessToken, descriptor);
118
132
  }
119
- return this._organizations.get(accessToken);
133
+ return organizations.get(accessToken);
134
+ }
135
+ async fetchOrganization(session) {
136
+ const organization = await (await this.getProvidersApi()).getLinearOrganization(toTokenWithInfo(this.id, session));
137
+ return organization && {
138
+ id: organization.id,
139
+ key: organization.key,
140
+ name: organization.name,
141
+ url: organization.url
142
+ };
120
143
  }
121
144
  /** Expires, and is dropped on a re-sync, so a team created or left mid-session is picked up (#5907). */
122
145
  _teams = new DiscoveryCache();
@@ -1 +1 @@
1
- {"version":3,"file":"linear.js","names":[],"sources":["../../../../../plus/integrations/src/providers/linear.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScopeFailure, GitIssueState } from '@gitkraken/provider-apis';\nimport * as l10n from '@vscode/l10n';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '@gitlens/git/models/autolink.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestType } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { IssueResourceDescriptor, ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { isIssueResourceDescriptor } from '@gitlens/git/utils/resourceDescriptor.utils.js';\nimport { chunk } from '@gitlens/utils/array.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { mapSettledBounded } from '@gitlens/utils/promise.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { IssuesCloudHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport { IntegrationReadUnavailableError } from '../errors.js';\nimport type { IssueEtagFields } from '../models/integration.js';\nimport type {\n\tAccountWideIssuesResult,\n\tIssuesForProjectOptions,\n\tProjectIssuesDrain,\n\tProjectIssuesRequest,\n\tSearchMyIssuesOptions,\n} from '../models/issueReads.js';\nimport {\n\tgroupProjectIssuesSearches,\n\tIssuesIntegration,\n\tsplitProjectIssuesSearch,\n} from '../models/issuesIntegration.js';\nimport type { LinearIssueEtagNode } from './linearIssuesEtag.js';\nimport { linearIssuesEtagMaxNumbers, toLinearIssueEtagFields } from './linearIssuesEtag.js';\nimport type { ProviderApiCollectionResult, ProviderIssue } from './models.js';\nimport { fromProviderIssue, providersMetadata, toIssueShape, toProviderIssueStates } from './models.js';\nimport { DiscoveryCache, discoveryCacheTtl } from './utils/discoveryCache.js';\nimport { mergeCollectionMetadata } from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[IssuesCloudHostIntegrationId.Linear];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\nconst maxPagesPerRequest = 10;\n/**\n * The account-wide drain's own backstop, separate from {@link maxPagesPerRequest}.\n *\n * Raised to 50 (5,000 issues at the SDK's 100-per-page) because 10 no longer means what it used to. That read\n * once fanned out over four overlapping relationship queries and merged them, so most of what a page cost was\n * rows already returned — a low budget capped the waste. It is now a single server-ordered query over an `or`\n * filter, so every page is 100 issues the caller has not seen, and the same budget just truncates real results\n * at 1,000.\n *\n * Reaching it is reported rather than silent: {@link LinearIntegration.searchProviderMyIssuesWithTruncation}\n * returns `truncated`, so the facade can say the read was incomplete instead of publishing a capped list as a\n * whole account. The bound stays — a runaway cursor must not spend requests forever — but it is now set where a\n * real Linear account is unlikely to reach it rather than where the duplication used to hurt.\n */\nconst maxAccountWidePagesPerRequest = 50;\nconst linearImplicitTeamsPageSize = 50;\n/** A team key and issue number, as Linear writes an issue's identifier (`ENG-123`). */\nconst linearIdentifier = /^([A-Za-z0-9]+)-(\\d+)$/;\n\nexport interface LinearTeamDescriptor extends IssueResourceDescriptor {\n\tavatarUrl: string | undefined;\n}\n\nexport interface LinearOrganizationDescriptor extends IssueResourceDescriptor {\n\turl: string;\n}\n\nexport interface LinearProjectDescriptor extends IssueResourceDescriptor {}\n\nexport class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Linear> {\n\t/**\n\t * Built from the teams, so it follows them: rebuilt when the team list changes or the caches are dropped, and once\n\t * older than {@link discoveryCacheTtl}. An expired set is still served while it is rebuilt in the background, because\n\t * autolinks are read on every render of a commit message and must not wait on the network (#5907).\n\t */\n\tprivate readonly _autolinks = new Map<\n\t\tstring,\n\t\t{ autolinks: (AutolinkReference | DynamicAutolinkReference)[]; builtAt: number }\n\t>();\n\tprivate readonly _autolinksBuilds = new Map<string, Promise<(AutolinkReference | DynamicAutolinkReference)[]>>();\n\n\toverride async autolinks(): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected || this._session == null) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst session = this._session;\n\t\tconst cached = this._autolinks.get(session.accessToken);\n\t\t// A session kept through a failed refresh is expired: building with it would send that token to the provider\n\t\t// on every render, so serve whatever was built before (none after a forced re-sync) until a read refreshes it.\n\t\tif (this.connectionExpired === true) return cached?.autolinks ?? [];\n\t\tif (cached == null) return this.buildAutolinks(session);\n\n\t\tif (Date.now() - cached.builtAt >= discoveryCacheTtl) {\n\t\t\tvoid this.buildAutolinks(session).catch(() => {});\n\t\t}\n\t\treturn cached.autolinks;\n\t}\n\n\t/** One build per token at a time: concurrent renders share it rather than each re-reading the teams. */\n\tprivate buildAutolinks(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst { accessToken } = session;\n\t\tlet build = this._autolinksBuilds.get(accessToken);\n\t\tif (build == null) {\n\t\t\tbuild = this.buildAutolinksCore(session).finally(() => this._autolinksBuilds.delete(accessToken));\n\t\t\tthis._autolinksBuilds.set(accessToken, build);\n\t\t}\n\t\treturn build;\n\t}\n\n\tprivate async buildAutolinksCore(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst generation = this._teams.generation;\n\t\tconst organization = await this.getOrganization(session);\n\t\tif (organization == null) return [];\n\n\t\tconst autolinks: (AutolinkReference | DynamicAutolinkReference)[] = [];\n\n\t\tconst teams = await this.getTeams(session);\n\t\tfor (const team of teams ?? []) {\n\t\t\tconst dashedPrefix = `${team.key}-`;\n\t\t\tconst underscoredPrefix = `${team.key}_`;\n\n\t\t\tautolinks.push({\n\t\t\t\tprefix: dashedPrefix,\n\t\t\t\turl: `${organization.url}/issue/${dashedPrefix}<num>`,\n\t\t\t\talphanumeric: false,\n\t\t\t\tignoreCase: false,\n\t\t\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, organization.name),\n\n\t\t\t\ttype: 'issue',\n\t\t\t\tdescription: l10n.t('{0} Issue {1}', organization.name, `${dashedPrefix}<num>`),\n\t\t\t\tdescriptor: { ...organization },\n\t\t\t});\n\t\t\tautolinks.push({\n\t\t\t\tprefix: underscoredPrefix,\n\t\t\t\turl: `${organization.url}/issue/${dashedPrefix}<num>`,\n\t\t\t\talphanumeric: false,\n\t\t\t\tignoreCase: false,\n\t\t\t\treferenceType: 'branch',\n\t\t\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, organization.name),\n\n\t\t\t\ttype: 'issue',\n\t\t\t\tdescription: l10n.t('{0} Issue {1}', organization.name, `${dashedPrefix}<num>`),\n\t\t\t\tdescriptor: { ...organization },\n\t\t\t});\n\t\t}\n\n\t\t// Not kept when built from teams read before the caches were dropped.\n\t\tif (this._teams.generation === generation) {\n\t\t\tthis._autolinks.set(session.accessToken, { autolinks: autolinks, builtAt: Date.now() });\n\t\t}\n\t\treturn autolinks;\n\t}\n\n\tprivate _organizations: Map<string, LinearOrganizationDescriptor | undefined> | undefined;\n\tprivate async getOrganization(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<LinearOrganizationDescriptor | undefined> {\n\t\tconst { accessToken } = session;\n\t\tthis._organizations ??= new Map<string, LinearOrganizationDescriptor | undefined>();\n\n\t\tconst cachedResources = this._organizations.get(accessToken);\n\n\t\tif (cachedResources == null || force) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst organization = await api.getLinearOrganization(toTokenWithInfo(this.id, session));\n\t\t\tconst descriptor: LinearOrganizationDescriptor | undefined = organization && {\n\t\t\t\tid: organization.id,\n\t\t\t\tkey: organization.key,\n\t\t\t\tname: organization.name,\n\t\t\t\turl: organization.url,\n\t\t\t};\n\t\t\tif (descriptor) {\n\t\t\t\tthis._organizations.set(accessToken, descriptor);\n\t\t\t}\n\t\t}\n\n\t\treturn this._organizations.get(accessToken);\n\t}\n\n\t/** Expires, and is dropped on a re-sync, so a team created or left mid-session is picked up (#5907). */\n\tprivate readonly _teams = new DiscoveryCache<LinearTeamDescriptor[]>();\n\tprivate async getTeams(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<LinearTeamDescriptor[] | undefined> {\n\t\treturn (await this.getTeamsWithMetadata(session, force))?.values;\n\t}\n\n\tprivate async getTeamsWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<ProviderApiCollectionResult<LinearTeamDescriptor> | undefined> {\n\t\tconst { accessToken } = session;\n\n\t\tconst cachedResources = this._teams.get(accessToken);\n\t\tif (cachedResources != null && !force) return { values: cachedResources };\n\n\t\tconst generation = this._teams.generation;\n\t\tconst api = await this.getProvidersApi();\n\t\tconst teams = await api.getLinearTeamsForCurrentUser(toTokenWithInfo(this.id, session));\n\t\tconst descriptors: LinearTeamDescriptor[] | undefined = teams?.map(t => ({\n\t\t\tid: t.id,\n\t\t\tkey: t.key,\n\t\t\tname: t.name,\n\t\t\tavatarUrl: t.iconUrl,\n\t\t}));\n\t\tif (descriptors == null) return undefined;\n\n\t\t// provider-apis currently requests Linear's teams connection without pageInfo or a cursor. Linear's\n\t\t// implicit page size is 50, so exactly a full page cannot prove there is no team 51. Preserve the useful\n\t\t// prefix, but mark it unknown and leave it uncached so the next read retries instead of treating it as\n\t\t// authoritative. Once provider-apis exposes a paged primitive, replace this fail-closed guard with a drain.\n\t\tif (descriptors.length >= linearImplicitTeamsPageSize) {\n\t\t\treturn { values: descriptors, metadata: { completeness: 'unknown' } };\n\t\t}\n\n\t\tif (this._teams.set(accessToken, descriptors, { generation: generation })) {\n\t\t\t// Built from the teams; a new set means new prefixes.\n\t\t\tthis._autolinks.delete(accessToken);\n\t\t}\n\t\treturn { values: descriptors };\n\t}\n\n\tprotected override async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\tconst organization = await this.getOrganization(session);\n\t\treturn organization != null ? [organization] : undefined;\n\t}\n\tprotected override getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\treturn this.getTeams(session);\n\t}\n\tprotected override async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ProviderApiCollectionResult<ResourceDescriptor>> {\n\t\treturn (await this.getTeamsWithMetadata(session)) ?? { values: [] };\n\t}\n\n\toverride invalidateDiscoveryCaches(): void {\n\t\tthis._organizations = undefined;\n\t\tthis._teams.clear();\n\t\tthis._autolinks.clear();\n\t}\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// Linear's viewer isn't a ProviderAccount (no username/avatar), so build the Account manually\n\t\t// (Trello-style) from the fields the viewer query returns.\n\t\tconst user = await api.getLinearCurrentUser(toTokenWithInfo(this.id, session));\n\t\tif (user == null) return undefined;\n\n\t\treturn {\n\t\t\tprovider: this,\n\t\t\tid: user.id,\n\t\t\tname: user.name ?? user.displayName ?? undefined,\n\t\t\tusername: user.displayName ?? undefined,\n\t\t\temail: user.email ?? undefined,\n\t\t\tavatarUrl: undefined,\n\t\t};\n\t}\n\n\tprotected override async getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: ResourceDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;\n\t}\n\n\tprotected override async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: ResourceDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tif (!isIssueResourceDescriptor(project)) return undefined;\n\n\t\t// Scope to \"my issues\" by the viewer's stable id, not the passed display name: Linear's `name` (full name)\n\t\t// and `displayName` (nickname) are distinct fields, and assignees are normalized with `.name` = the full\n\t\t// name while the caller's `user` is the displayName — so a name string can miss. The assignee `.id` is the\n\t\t// Linear user id, which is unambiguous.\n\t\tlet viewerId: string | undefined;\n\t\tif (options?.user != null) {\n\t\t\tviewerId = await this.getViewerId(session, options);\n\t\t\t// If the viewer can't be resolved we can't scope to \"my issues\" — returning the unfiltered team\n\t\t\t// issues would leak everyone else's, and returning [] is indistinguishable from \"no issues assigned\n\t\t\t// to me\". Throw so the facade (getIssuesForProjectResult → runCaptured) surfaces a warning +\n\t\t\t// fetchFailed the caller can act on, instead of a silent empty.\n\t\t\tif (viewerId == null) {\n\t\t\t\tthrow new IntegrationReadUnavailableError(\n\t\t\t\t\tmetadata.name,\n\t\t\t\t\t'could not resolve the current user to scope issues to',\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// `getProviderProjectsForResources` returns Linear teams, so `project.id` is a team id here.\n\t\t//\n\t\t// The state and the viewer are narrowed server-side, not after the drain: a team's done work and other\n\t\t// people's issues would otherwise count against the backstop, so a busy team runs out of pages before the\n\t\t// viewer's open issues and hands back a page that looks complete.\n\t\tconst drain = await this.drainIssues(\n\t\t\tsession,\n\t\t\t{\n\t\t\t\tteams: [project.id],\n\t\t\t\t...(viewerId != null ? { assignees: [viewerId] } : {}),\n\t\t\t\tstates: toProviderIssueStates(options?.state),\n\t\t\t},\n\t\t\toptions?.sort,\n\t\t\t{ providerId: this.id, projectId: project.id },\n\t\t);\n\n\t\treturn drain.truncated\n\t\t\t? { values: drain.issues, truncated: true, recovery: 'none', metadata: drain.metadata }\n\t\t\t: { values: drain.issues, truncated: false, metadata: drain.metadata };\n\t}\n\n\t/**\n\t * Searches every team of a user-scoped read together: one `getIssues` names all of them and the viewer as\n\t * assignee, so a team with none of the user's issues costs no request of its own. Linear's filter is a GraphQL\n\t * body, not a query string, so there is no key bound to chunk at.\n\t */\n\tprotected override getProjectIssuesSearches(\n\t\trequests: readonly ProjectIssuesRequest<ResourceDescriptor>[],\n\t): number[][] | undefined {\n\t\treturn groupProjectIssuesSearches(requests, ({ project, options }) =>\n\t\t\toptions.user != null && isIssueResourceDescriptor(project)\n\t\t\t\t? [options.user, options.userId ?? '', options.sort ?? '', options.state ?? ''].join('\\0')\n\t\t\t\t: undefined,\n\t\t);\n\t}\n\n\tprotected override async searchProviderProjectIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trequests: readonly ProjectIssuesRequest<ResourceDescriptor>[],\n\t): Promise<IssueShape[][] | undefined> {\n\t\t// Every request of a search shares its user scope (see `getProjectIssuesSearches`).\n\t\tconst { options } = requests[0];\n\t\tif (options.user == null) return undefined;\n\n\t\t// Unresolvable here means each team's own read throws the same warning, so let them.\n\t\tconst viewerId = await this.getViewerId(session, options);\n\t\tif (viewerId == null) return undefined;\n\n\t\tconst teams = requests.map(request => request.project).filter(isIssueResourceDescriptor);\n\t\tif (teams.length !== requests.length) return undefined;\n\n\t\tconst drain = await this.drainIssues(\n\t\t\tsession,\n\t\t\t{\n\t\t\t\tteams: teams.map(team => team.id),\n\t\t\t\tassignees: [viewerId],\n\t\t\t\tstates: toProviderIssueStates(options.state),\n\t\t\t},\n\t\t\toptions.sort,\n\t\t\t// Never published: an incomplete search is read again per team, which records its own failures.\n\t\t\t{ providerId: this.id },\n\t\t);\n\t\t// Deliberately discarded rather than served: an incomplete search says nothing about which of its teams it\n\t\t// covered, so only the per-team reads can report each one's completeness. The waste is bounded by one team's\n\t\t// page budget, and paid only by a search that needed more than that or failed partway.\n\t\tif (drain.truncated || (drain.metadata != null && drain.metadata.completeness !== 'complete')) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// A normalized Linear issue carries its team only inside `project`, which is absent for an issue in no\n\t\t// Linear project, so split by the identifier instead: Linear numbers every issue `<team key>-<n>`, and\n\t\t// renumbers it under its new team's key when it moves.\n\t\treturn splitProjectIssuesSearch(\n\t\t\tteams.map(team => team.key),\n\t\t\tdrain.issues,\n\t\t\tissue => {\n\t\t\t\tconst separator = issue.id.lastIndexOf('-');\n\t\t\t\treturn separator > 0 ? issue.id.slice(0, separator) : undefined;\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate readonly _viewerIds = new PromiseCache<string, string | undefined>({ capacity: 10 });\n\t/**\n\t * The viewer's Linear user id: the caller's resolved `userId` when it has one, else the viewer query, shared per\n\t * token. A tracker read hands each team the id of the team's own resource, which Linear's team descriptors don't\n\t * name, so without sharing every team read (and a search's fallback) would ask for the viewer again.\n\t */\n\tprivate getViewerId(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: IssuesForProjectOptions,\n\t): Promise<string | undefined> {\n\t\tif (options.userId) return Promise.resolve(options.userId);\n\n\t\treturn this._viewerIds.getOrCreate(\n\t\t\tsession.accessToken,\n\t\t\tasync () => {\n\t\t\t\tconst api = await this.getProvidersApi();\n\t\t\t\treturn (await api.getLinearCurrentUser(toTokenWithInfo(this.id, session)))?.id;\n\t\t\t},\n\t\t\t// Only a resolved id is worth keeping; an unresolved viewer is asked for again next time, as is a failure.\n\t\t\t{ evictWhen: id => id == null },\n\t\t);\n\t}\n\n\t/**\n\t * Follows `getIssues`' cursor up to {@link maxPagesPerRequest}. `truncated` is set when that backstop stopped\n\t * the drain with more pages still available, when the provider stalled its cursor or flagged its paging as\n\t * truncated, or when a page after the first failed, which is recorded at `failureScope` while the prefix is\n\t * kept. A first-page failure throws, so the caller sees a hard error rather than an empty success.\n\t */\n\tprivate async drainIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tfilter: { teams: string[]; assignees?: string[]; states?: GitIssueState[] },\n\t\tsort: IssuesForProjectOptions['sort'],\n\t\tfailureScope: CollectionScopeFailure['scope'],\n\t): Promise<{ issues: IssueShape[]; truncated: boolean; metadata?: CollectionMetadata }> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst assignees = filter.assignees?.length ? filter.assignees : undefined;\n\t\tlet cursor: string | undefined;\n\t\tlet hasMore: boolean;\n\t\tlet requestCount = 0;\n\t\tlet truncated = false;\n\t\tlet collectionMetadata: CollectionMetadata | undefined;\n\t\tconst issues: IssueShape[] = [];\n\t\tdo {\n\t\t\tlet result: Awaited<ReturnType<typeof api.getLinearIssues>>;\n\t\t\ttry {\n\t\t\t\tresult = await api.getLinearIssues(toTokenWithInfo(this.id, session), filter, {\n\t\t\t\t\tcursor: cursor,\n\t\t\t\t\tsort: sort,\n\t\t\t\t});\n\t\t\t} catch (ex) {\n\t\t\t\tif (issues.length === 0) throw ex;\n\n\t\t\t\ttruncated = true;\n\t\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, {\n\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\tfailures: [toCollectionScopeFailure(failureScope, ex)],\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trequestCount += 1;\n\t\t\thasMore = result.paging?.more ?? false;\n\t\t\tconst nextCursor = result.paging?.cursor;\n\t\t\ttruncated ||= result.paging?.truncated === true;\n\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, result.metadata);\n\t\t\tfor (const issue of result.values) {\n\t\t\t\tconst shape = toIssueShape(issue, this, { projection: 'project' });\n\t\t\t\t// The query already scopes to the assignees; checking again keeps another user's issue out of \"my\n\t\t\t\t// issues\" should the provider ever return one.\n\t\t\t\tif (shape != null && (assignees == null || shape.assignees?.some(a => assignees.includes(a.id)))) {\n\t\t\t\t\tissues.push(shape);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The provider claims more but returns no advancing cursor: we can't continue without re-reading the\n\t\t\t// same page, so the drain is incomplete — flag it rather than silently stopping.\n\t\t\tif (hasMore && (nextCursor == null || nextCursor === cursor)) {\n\t\t\t\ttruncated = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcursor = nextCursor;\n\t\t\tif (hasMore && requestCount >= maxPagesPerRequest) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t} while (requestCount < maxPagesPerRequest && hasMore);\n\n\t\treturn { issues: issues, truncated: truncated, metadata: collectionMetadata };\n\t}\n\n\toverride get id(): IssuesCloudHostIntegrationId.Linear {\n\t\treturn IssuesCloudHostIntegrationId.Linear;\n\t}\n\tprotected override get key(): 'linear' {\n\t\treturn 'linear';\n\t}\n\toverride get name(): string {\n\t\treturn metadata.name;\n\t}\n\toverride get domain(): string {\n\t\treturn metadata.domain;\n\t}\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, resources, cancellation))?.values;\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for Linear, drained to exhaustion and reporting whether it got there.\n\t *\n\t * The SDK read is one server-ordered query over an `or` filter across the four relationships (assigned,\n\t * created, named in an issue's content, named in a comment), so each page is 100 issues the caller has not\n\t * seen and the pages are ordered as one sequence. Draining it is therefore just following the cursor.\n\t *\n\t * Overridden rather than left to the base default because that default hardcodes `truncated: false`, and a\n\t * bounded drain that stops early is exactly the case a caller must be able to see. Three things end the\n\t * drain, and only one of them is completion:\n\t *\n\t * - the provider says there is no next page — complete;\n\t * - the backstop is reached — `truncated`, because issues beyond it exist and were not read;\n\t * - the provider claims a next page but hands back a cursor that does not advance — also `truncated`, since\n\t * the read cannot continue and the remainder is unreachable rather than absent.\n\t *\n\t * Cancellation is deliberately NOT truncation: the caller asked for the read to stop, so the partial list is\n\t * what it asked for, and flagging it would surface an incompleteness warning for a user action.\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tif (resources != null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst states = toProviderIssueStates(options?.state);\n\t\tlet cursor = undefined;\n\t\t// Starts false so an immediate cancellation, which leaves the loop before the first response, reads as\n\t\t// \"no more pages known\" rather than as an unfinished drain.\n\t\tlet hasMore = false;\n\t\tlet requestCount = 0;\n\t\tlet truncated = false;\n\t\tconst issues = [];\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tif (cancellation?.aborted) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst result = await api.getIssuesForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\t\t\tcursor: cursor,\n\t\t\t\t\tstates: states,\n\t\t\t\t\tsort: options?.sort,\n\t\t\t\t});\n\t\t\t\trequestCount += 1;\n\t\t\t\thasMore = result.paging?.more ?? false;\n\t\t\t\tconst nextCursor = result.paging?.cursor;\n\n\t\t\t\t// Keep this page before deciding whether to continue: the request is already paid for, so\n\t\t\t\t// dropping its rows would lose real results to save nothing.\n\t\t\t\tconst formattedIssues = result.values\n\t\t\t\t\t.map(issue => toIssueShape(issue, this, { projection: 'account' }))\n\t\t\t\t\t.filter((result): result is IssueShape => result != null);\n\t\t\t\tif (formattedIssues.length > 0) {\n\t\t\t\t\tissues.push(...formattedIssues);\n\t\t\t\t}\n\n\t\t\t\t// The provider claims more but hands back no advancing cursor: continuing would re-read the page\n\t\t\t\t// just returned. `ProvidersApi.getPagedResult` already normalizes this into `more: false`, so\n\t\t\t\t// this is a backstop for if that ever stops holding rather than a case seen today -- but the rest\n\t\t\t\t// of the account is unreachable either way, which is what `truncated` says.\n\t\t\t\tif (hasMore && (nextCursor == null || nextCursor === cursor)) {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcursor = nextCursor;\n\t\t\t} while (requestCount < maxAccountWidePagesPerRequest && hasMore);\n\n\t\t\t// Ran out of budget with pages still to come.\n\t\t\tif (hasMore && requestCount >= maxAccountWidePagesPerRequest) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (issues.length === 0) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\t// Kept what was already fetched, so the list is real but short of the account.\n\t\t\ttruncated = true;\n\t\t\tLogger.error(ex, 'searchProviderMyIssues');\n\t\t}\n\t\treturn { values: issues, truncated: truncated };\n\t}\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\t{ key }: { id: string; key: string },\n\t\t_type: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst issue = await this.getRawProviderIssue(session, resource, key);\n\t\tconst autolinkableIssue: ProviderIssue | undefined = issue && {\n\t\t\t...issue,\n\t\t\turl: this.getIssueAutolinkLikeUrl(issue),\n\t\t};\n\t\treturn autolinkableIssue && toIssueShape(autolinkableIssue, this, { projection: 'point' });\n\t}\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst result = await this.getRawProviderIssue(session, resource, id);\n\t\treturn result && fromProviderIssue(result, this, { projection: 'point' });\n\t}\n\n\tprotected override async getProviderIssueByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresourceId: string,\n\t\tid: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst result = await api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resourceId,\n\t\t\tnumber: id,\n\t\t});\n\t\treturn result && fromProviderIssue(result, this, { projection: 'batch' });\n\t}\n\n\t/**\n\t * The cheap check behind the batch issue read's etags: one `issues` query per team and\n\t * {@link linearIssuesEtagMaxNumbers} distinct numbers, where {@link getProviderIssueByResourceId} sends one request\n\t * per issue. A request that throws rejects only its own targets' slots, with its error classified as the full\n\t * read's would be.\n\t *\n\t * Never proves an absence. The query filters by the team's CURRENT key and asks for archived issues too, but the\n\t * full read's `issue(id:)` may also resolve an identifier the issue no longer carries (its team was renamed, or it\n\t * moved to another team), so an issue the query leaves out may still exist. Its target's slot is rejected, as is a\n\t * target whose identifier isn't a team key and a number, so the full read decides. When no target was answered and\n\t * nothing failed, the check declines instead, so a batch of only such targets costs a full read, not a failure.\n\t */\n\tprotected override async getProviderIssuesEtagFieldsByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined> {\n\t\tconst slots = new Array<PromiseSettledResult<IssueEtagFields | undefined>>(targets.length);\n\t\tlet answered = false;\n\t\tlet failed = false;\n\n\t\t// Each workspace and team's distinct numbers, with the targets asking for each.\n\t\tconst byTeam = new Map<string, { teamKey: string; numbers: Map<number, number[]> }>();\n\t\tfor (const [index, target] of targets.entries()) {\n\t\t\tconst match = linearIdentifier.exec(target.identifier);\n\t\t\tconst number = match != null ? Number(match[2]) : Number.NaN;\n\t\t\t// Only a number written as Linear writes it, so `ENG-012` never answers for `ENG-12`.\n\t\t\tif (match == null || !Number.isSafeInteger(number) || String(number) !== match[2]) {\n\t\t\t\tslots[index] = {\n\t\t\t\t\tstatus: 'rejected',\n\t\t\t\t\treason: new Error(`Not a Linear issue identifier: ${target.identifier}`),\n\t\t\t\t};\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst teamKey = match[1].toUpperCase();\n\t\t\tconst groupKey = `${target.resourceId}\\n${teamKey}`;\n\t\t\tlet group = byTeam.get(groupKey);\n\t\t\tif (group == null) {\n\t\t\t\tgroup = { teamKey: teamKey, numbers: new Map() };\n\t\t\t\tbyTeam.set(groupKey, group);\n\t\t\t}\n\n\t\t\tlet indices = group.numbers.get(number);\n\t\t\tif (indices == null) {\n\t\t\t\tindices = [];\n\t\t\t\tgroup.numbers.set(number, indices);\n\t\t\t}\n\t\t\tindices.push(index);\n\t\t}\n\n\t\tconst requests = [...byTeam.values()].flatMap(({ teamKey, numbers }) =>\n\t\t\tchunk([...numbers], linearIssuesEtagMaxNumbers).map(entries => ({ teamKey: teamKey, entries: entries })),\n\t\t);\n\t\tif (requests.length) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t\tconst answers = await mapSettledBounded(requests, providerFanOutConcurrency, r =>\n\t\t\t\tapi.getLinearIssuesEtagFields(\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\tr.teamKey,\n\t\t\t\t\tr.entries.map(([number]) => number),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tfor (const [i, answer] of answers.entries()) {\n\t\t\t\tconst { teamKey, entries } = requests[i];\n\t\t\t\tif (answer.status === 'rejected') {\n\t\t\t\t\tfailed = true;\n\t\t\t\t\tfor (const [, indices] of entries) {\n\t\t\t\t\t\tfor (const index of indices) {\n\t\t\t\t\t\t\tslots[index] = answer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// By number, never by position: Linear returns the issues in its own order, and only the ones it found.\n\t\t\t\tconst byNumber = new Map<number, LinearIssueEtagNode>();\n\t\t\t\tfor (const issue of answer.value) {\n\t\t\t\t\tbyNumber.set(issue.number, issue);\n\t\t\t\t}\n\n\t\t\t\tfor (const [number, indices] of entries) {\n\t\t\t\t\tconst issue = byNumber.get(number);\n\t\t\t\t\tlet slot: PromiseSettledResult<IssueEtagFields | undefined>;\n\t\t\t\t\tif (issue == null) {\n\t\t\t\t\t\tslot = { status: 'rejected', reason: new Error(`Linear did not return ${teamKey}-${number}`) };\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tslot = { status: 'fulfilled', value: toLinearIssueEtagFields(issue) };\n\t\t\t\t\t\t\tanswered = true;\n\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\tslot = { status: 'rejected', reason: ex };\n\t\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const index of indices) {\n\t\t\t\t\t\tslots[index] = slot;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn answered || failed ? slots : undefined;\n\t}\n\n\tprivate async getRawProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<ProviderIssue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif (!isIssueResourceDescriptor(resource)) {\n\t\t\tLogger.error(undefined, 'getProviderIssue: resource is not an IssueResourceDescriptor');\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resource.id,\n\t\t\tnumber: id,\n\t\t});\n\t}\n\tprivate getIssueAutolinkLikeUrl(issue: ProviderIssue): string | null {\n\t\tconst url = issue.url;\n\t\tif (url == null) return null;\n\n\t\tconst lastSegment = url.split('/').pop();\n\t\tif (!lastSegment || issue.number === lastSegment) {\n\t\t\treturn url;\n\t\t}\n\t\treturn url.substring(0, url.length - lastSegment.length - 1);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"linear.js","names":[],"sources":["../../../../../plus/integrations/src/providers/linear.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScopeFailure, GitIssueState } from '@gitkraken/provider-apis';\nimport * as l10n from '@vscode/l10n';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '@gitlens/git/models/autolink.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestType } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { IssueResourceDescriptor, ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { isIssueResourceDescriptor } from '@gitlens/git/utils/resourceDescriptor.utils.js';\nimport { chunk } from '@gitlens/utils/array.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { mapSettledBounded } from '@gitlens/utils/promise.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { ConnectionResource, ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { IssuesCloudHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport { IntegrationReadUnavailableError } from '../errors.js';\nimport type { IssueEtagFields } from '../models/integration.js';\nimport type {\n\tAccountWideIssuesResult,\n\tIssuesForProjectOptions,\n\tProjectIssuesDrain,\n\tProjectIssuesRequest,\n\tSearchMyIssuesOptions,\n} from '../models/issueReads.js';\nimport {\n\tgroupProjectIssuesSearches,\n\tIssuesIntegration,\n\tsplitProjectIssuesSearch,\n} from '../models/issuesIntegration.js';\nimport type { LinearIssueEtagNode } from './linearIssuesEtag.js';\nimport { linearIssuesEtagMaxNumbers, toLinearIssueEtagFields } from './linearIssuesEtag.js';\nimport type { ProviderApiCollectionResult, ProviderIssue } from './models.js';\nimport { fromProviderIssue, providersMetadata, toIssueShape, toProviderIssueStates } from './models.js';\nimport { DiscoveryCache, discoveryCacheTtl } from './utils/discoveryCache.js';\nimport { mergeCollectionMetadata } from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[IssuesCloudHostIntegrationId.Linear];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\nconst maxPagesPerRequest = 10;\n/**\n * The account-wide drain's own backstop, separate from {@link maxPagesPerRequest}.\n *\n * Raised to 50 (5,000 issues at the SDK's 100-per-page) because 10 no longer means what it used to. That read\n * once fanned out over four overlapping relationship queries and merged them, so most of what a page cost was\n * rows already returned — a low budget capped the waste. It is now a single server-ordered query over an `or`\n * filter, so every page is 100 issues the caller has not seen, and the same budget just truncates real results\n * at 1,000.\n *\n * Reaching it is reported rather than silent: {@link LinearIntegration.searchProviderMyIssuesWithTruncation}\n * returns `truncated`, so the facade can say the read was incomplete instead of publishing a capped list as a\n * whole account. The bound stays — a runaway cursor must not spend requests forever — but it is now set where a\n * real Linear account is unlikely to reach it rather than where the duplication used to hurt.\n */\nconst maxAccountWidePagesPerRequest = 50;\nconst linearImplicitTeamsPageSize = 50;\n/** A team key and issue number, as Linear writes an issue's identifier (`ENG-123`). */\nconst linearIdentifier = /^([A-Za-z0-9]+)-(\\d+)$/;\n\nexport interface LinearTeamDescriptor extends IssueResourceDescriptor {\n\tavatarUrl: string | undefined;\n}\n\nexport interface LinearOrganizationDescriptor extends IssueResourceDescriptor {\n\turl: string;\n}\n\nexport interface LinearProjectDescriptor extends IssueResourceDescriptor {}\n\nexport class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Linear> {\n\t/**\n\t * Built from the teams, so it follows them: rebuilt when the team list changes or the caches are dropped, and once\n\t * older than {@link discoveryCacheTtl}. An expired set is still served while it is rebuilt in the background, because\n\t * autolinks are read on every render of a commit message and must not wait on the network (#5907).\n\t */\n\tprivate readonly _autolinks = new Map<\n\t\tstring,\n\t\t{ autolinks: (AutolinkReference | DynamicAutolinkReference)[]; builtAt: number }\n\t>();\n\tprivate readonly _autolinksBuilds = new Map<string, Promise<(AutolinkReference | DynamicAutolinkReference)[]>>();\n\n\toverride async autolinks(): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected || this._session == null) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst session = this._session;\n\t\tconst cached = this._autolinks.get(session.accessToken);\n\t\t// A session kept through a failed refresh is expired: building with it would send that token to the provider\n\t\t// on every render, so serve whatever was built before (none after a forced re-sync) until a read refreshes it.\n\t\tif (this.connectionExpired === true) return cached?.autolinks ?? [];\n\t\tif (cached == null) return this.buildAutolinks(session);\n\n\t\tif (Date.now() - cached.builtAt >= discoveryCacheTtl) {\n\t\t\tvoid this.buildAutolinks(session).catch(() => {});\n\t\t}\n\t\treturn cached.autolinks;\n\t}\n\n\t/** One build per token at a time: concurrent renders share it rather than each re-reading the teams. */\n\tprivate buildAutolinks(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst { accessToken } = session;\n\t\tlet build = this._autolinksBuilds.get(accessToken);\n\t\tif (build == null) {\n\t\t\tbuild = this.buildAutolinksCore(session).finally(() => this._autolinksBuilds.delete(accessToken));\n\t\t\tthis._autolinksBuilds.set(accessToken, build);\n\t\t}\n\t\treturn build;\n\t}\n\n\tprivate async buildAutolinksCore(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst generation = this._teams.generation;\n\t\tconst organization = await this.getOrganization(session);\n\t\tif (organization == null) return [];\n\n\t\tconst autolinks: (AutolinkReference | DynamicAutolinkReference)[] = [];\n\n\t\tconst teams = await this.getTeams(session);\n\t\tfor (const team of teams ?? []) {\n\t\t\tconst dashedPrefix = `${team.key}-`;\n\t\t\tconst underscoredPrefix = `${team.key}_`;\n\n\t\t\tautolinks.push({\n\t\t\t\tprefix: dashedPrefix,\n\t\t\t\turl: `${organization.url}/issue/${dashedPrefix}<num>`,\n\t\t\t\talphanumeric: false,\n\t\t\t\tignoreCase: false,\n\t\t\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, organization.name),\n\n\t\t\t\ttype: 'issue',\n\t\t\t\tdescription: l10n.t('{0} Issue {1}', organization.name, `${dashedPrefix}<num>`),\n\t\t\t\tdescriptor: { ...organization },\n\t\t\t});\n\t\t\tautolinks.push({\n\t\t\t\tprefix: underscoredPrefix,\n\t\t\t\turl: `${organization.url}/issue/${dashedPrefix}<num>`,\n\t\t\t\talphanumeric: false,\n\t\t\t\tignoreCase: false,\n\t\t\t\treferenceType: 'branch',\n\t\t\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, organization.name),\n\n\t\t\t\ttype: 'issue',\n\t\t\t\tdescription: l10n.t('{0} Issue {1}', organization.name, `${dashedPrefix}<num>`),\n\t\t\t\tdescriptor: { ...organization },\n\t\t\t});\n\t\t}\n\n\t\t// Not kept when built from teams read before the caches were dropped.\n\t\tif (this._teams.generation === generation) {\n\t\t\tthis._autolinks.set(session.accessToken, { autolinks: autolinks, builtAt: Date.now() });\n\t\t}\n\t\treturn autolinks;\n\t}\n\n\t/** The workspace is the token's organization; kept only as a complete, trimmed pair. */\n\toverride async getProviderResourceForSession(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ConnectionResource | undefined> {\n\t\tconst organization = await this.getOrganization(session, true);\n\t\tconst id = organization?.id.trim();\n\t\tconst name = organization?.name.trim();\n\t\treturn id && name ? { id: id, name: name } : undefined;\n\t}\n\n\tprivate _organizations: Map<string, LinearOrganizationDescriptor | undefined> | undefined;\n\t/**\n\t * In flight by token. The request can't be aborted (provider-apis takes no signal), so a lookup that outlives its\n\t * caller's timeout is joined by the next one rather than piling another request on top of it.\n\t */\n\tprivate readonly _organizationRequests = new Map<string, Promise<LinearOrganizationDescriptor | undefined>>();\n\tprivate async getOrganization(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<LinearOrganizationDescriptor | undefined> {\n\t\tconst { accessToken } = session;\n\t\t// Held across the await: a re-sync drops `_organizations` meanwhile, and this read must still complete.\n\t\tconst organizations = (this._organizations ??= new Map<string, LinearOrganizationDescriptor | undefined>());\n\n\t\tconst cachedResources = organizations.get(accessToken);\n\n\t\tif (cachedResources == null || force) {\n\t\t\tlet request = this._organizationRequests.get(accessToken);\n\t\t\tif (request == null) {\n\t\t\t\trequest = this.fetchOrganization(session).finally(() => this._organizationRequests.delete(accessToken));\n\t\t\t\tthis._organizationRequests.set(accessToken, request);\n\t\t\t}\n\n\t\t\tconst descriptor = await request;\n\t\t\tif (descriptor) {\n\t\t\t\torganizations.set(accessToken, descriptor);\n\t\t\t}\n\t\t}\n\n\t\treturn organizations.get(accessToken);\n\t}\n\n\tprivate async fetchOrganization(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<LinearOrganizationDescriptor | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst organization = await api.getLinearOrganization(toTokenWithInfo(this.id, session));\n\t\treturn (\n\t\t\torganization && {\n\t\t\t\tid: organization.id,\n\t\t\t\tkey: organization.key,\n\t\t\t\tname: organization.name,\n\t\t\t\turl: organization.url,\n\t\t\t}\n\t\t);\n\t}\n\n\t/** Expires, and is dropped on a re-sync, so a team created or left mid-session is picked up (#5907). */\n\tprivate readonly _teams = new DiscoveryCache<LinearTeamDescriptor[]>();\n\tprivate async getTeams(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<LinearTeamDescriptor[] | undefined> {\n\t\treturn (await this.getTeamsWithMetadata(session, force))?.values;\n\t}\n\n\tprivate async getTeamsWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<ProviderApiCollectionResult<LinearTeamDescriptor> | undefined> {\n\t\tconst { accessToken } = session;\n\n\t\tconst cachedResources = this._teams.get(accessToken);\n\t\tif (cachedResources != null && !force) return { values: cachedResources };\n\n\t\tconst generation = this._teams.generation;\n\t\tconst api = await this.getProvidersApi();\n\t\tconst teams = await api.getLinearTeamsForCurrentUser(toTokenWithInfo(this.id, session));\n\t\tconst descriptors: LinearTeamDescriptor[] | undefined = teams?.map(t => ({\n\t\t\tid: t.id,\n\t\t\tkey: t.key,\n\t\t\tname: t.name,\n\t\t\tavatarUrl: t.iconUrl,\n\t\t}));\n\t\tif (descriptors == null) return undefined;\n\n\t\t// provider-apis currently requests Linear's teams connection without pageInfo or a cursor. Linear's\n\t\t// implicit page size is 50, so exactly a full page cannot prove there is no team 51. Preserve the useful\n\t\t// prefix, but mark it unknown and leave it uncached so the next read retries instead of treating it as\n\t\t// authoritative. Once provider-apis exposes a paged primitive, replace this fail-closed guard with a drain.\n\t\tif (descriptors.length >= linearImplicitTeamsPageSize) {\n\t\t\treturn { values: descriptors, metadata: { completeness: 'unknown' } };\n\t\t}\n\n\t\tif (this._teams.set(accessToken, descriptors, { generation: generation })) {\n\t\t\t// Built from the teams; a new set means new prefixes.\n\t\t\tthis._autolinks.delete(accessToken);\n\t\t}\n\t\treturn { values: descriptors };\n\t}\n\n\tprotected override async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\tconst organization = await this.getOrganization(session);\n\t\treturn organization != null ? [organization] : undefined;\n\t}\n\tprotected override getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\treturn this.getTeams(session);\n\t}\n\tprotected override async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ProviderApiCollectionResult<ResourceDescriptor>> {\n\t\treturn (await this.getTeamsWithMetadata(session)) ?? { values: [] };\n\t}\n\n\toverride invalidateDiscoveryCaches(): void {\n\t\tthis._organizations = undefined;\n\t\tthis._teams.clear();\n\t\tthis._autolinks.clear();\n\t}\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// Linear's viewer isn't a ProviderAccount (no username/avatar), so build the Account manually\n\t\t// (Trello-style) from the fields the viewer query returns.\n\t\tconst user = await api.getLinearCurrentUser(toTokenWithInfo(this.id, session));\n\t\tif (user == null) return undefined;\n\n\t\treturn {\n\t\t\tprovider: this,\n\t\t\tid: user.id,\n\t\t\tname: user.name ?? user.displayName ?? undefined,\n\t\t\tusername: user.displayName ?? undefined,\n\t\t\temail: user.email ?? undefined,\n\t\t\tavatarUrl: undefined,\n\t\t};\n\t}\n\n\tprotected override async getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: ResourceDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;\n\t}\n\n\tprotected override async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: ResourceDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tif (!isIssueResourceDescriptor(project)) return undefined;\n\n\t\t// Scope to \"my issues\" by the viewer's stable id, not the passed display name: Linear's `name` (full name)\n\t\t// and `displayName` (nickname) are distinct fields, and assignees are normalized with `.name` = the full\n\t\t// name while the caller's `user` is the displayName — so a name string can miss. The assignee `.id` is the\n\t\t// Linear user id, which is unambiguous.\n\t\tlet viewerId: string | undefined;\n\t\tif (options?.user != null) {\n\t\t\tviewerId = await this.getViewerId(session, options);\n\t\t\t// If the viewer can't be resolved we can't scope to \"my issues\" — returning the unfiltered team\n\t\t\t// issues would leak everyone else's, and returning [] is indistinguishable from \"no issues assigned\n\t\t\t// to me\". Throw so the facade (getIssuesForProjectResult → runCaptured) surfaces a warning +\n\t\t\t// fetchFailed the caller can act on, instead of a silent empty.\n\t\t\tif (viewerId == null) {\n\t\t\t\tthrow new IntegrationReadUnavailableError(\n\t\t\t\t\tmetadata.name,\n\t\t\t\t\t'could not resolve the current user to scope issues to',\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// `getProviderProjectsForResources` returns Linear teams, so `project.id` is a team id here.\n\t\t//\n\t\t// The state and the viewer are narrowed server-side, not after the drain: a team's done work and other\n\t\t// people's issues would otherwise count against the backstop, so a busy team runs out of pages before the\n\t\t// viewer's open issues and hands back a page that looks complete.\n\t\tconst drain = await this.drainIssues(\n\t\t\tsession,\n\t\t\t{\n\t\t\t\tteams: [project.id],\n\t\t\t\t...(viewerId != null ? { assignees: [viewerId] } : {}),\n\t\t\t\tstates: toProviderIssueStates(options?.state),\n\t\t\t},\n\t\t\toptions?.sort,\n\t\t\t{ providerId: this.id, projectId: project.id },\n\t\t);\n\n\t\treturn drain.truncated\n\t\t\t? { values: drain.issues, truncated: true, recovery: 'none', metadata: drain.metadata }\n\t\t\t: { values: drain.issues, truncated: false, metadata: drain.metadata };\n\t}\n\n\t/**\n\t * Searches every team of a user-scoped read together: one `getIssues` names all of them and the viewer as\n\t * assignee, so a team with none of the user's issues costs no request of its own. Linear's filter is a GraphQL\n\t * body, not a query string, so there is no key bound to chunk at.\n\t */\n\tprotected override getProjectIssuesSearches(\n\t\trequests: readonly ProjectIssuesRequest<ResourceDescriptor>[],\n\t): number[][] | undefined {\n\t\treturn groupProjectIssuesSearches(requests, ({ project, options }) =>\n\t\t\toptions.user != null && isIssueResourceDescriptor(project)\n\t\t\t\t? [options.user, options.userId ?? '', options.sort ?? '', options.state ?? ''].join('\\0')\n\t\t\t\t: undefined,\n\t\t);\n\t}\n\n\tprotected override async searchProviderProjectIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trequests: readonly ProjectIssuesRequest<ResourceDescriptor>[],\n\t): Promise<IssueShape[][] | undefined> {\n\t\t// Every request of a search shares its user scope (see `getProjectIssuesSearches`).\n\t\tconst { options } = requests[0];\n\t\tif (options.user == null) return undefined;\n\n\t\t// Unresolvable here means each team's own read throws the same warning, so let them.\n\t\tconst viewerId = await this.getViewerId(session, options);\n\t\tif (viewerId == null) return undefined;\n\n\t\tconst teams = requests.map(request => request.project).filter(isIssueResourceDescriptor);\n\t\tif (teams.length !== requests.length) return undefined;\n\n\t\tconst drain = await this.drainIssues(\n\t\t\tsession,\n\t\t\t{\n\t\t\t\tteams: teams.map(team => team.id),\n\t\t\t\tassignees: [viewerId],\n\t\t\t\tstates: toProviderIssueStates(options.state),\n\t\t\t},\n\t\t\toptions.sort,\n\t\t\t// Never published: an incomplete search is read again per team, which records its own failures.\n\t\t\t{ providerId: this.id },\n\t\t);\n\t\t// Deliberately discarded rather than served: an incomplete search says nothing about which of its teams it\n\t\t// covered, so only the per-team reads can report each one's completeness. The waste is bounded by one team's\n\t\t// page budget, and paid only by a search that needed more than that or failed partway.\n\t\tif (drain.truncated || (drain.metadata != null && drain.metadata.completeness !== 'complete')) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// A normalized Linear issue carries its team only inside `project`, which is absent for an issue in no\n\t\t// Linear project, so split by the identifier instead: Linear numbers every issue `<team key>-<n>`, and\n\t\t// renumbers it under its new team's key when it moves.\n\t\treturn splitProjectIssuesSearch(\n\t\t\tteams.map(team => team.key),\n\t\t\tdrain.issues,\n\t\t\tissue => {\n\t\t\t\tconst separator = issue.id.lastIndexOf('-');\n\t\t\t\treturn separator > 0 ? issue.id.slice(0, separator) : undefined;\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate readonly _viewerIds = new PromiseCache<string, string | undefined>({ capacity: 10 });\n\t/**\n\t * The viewer's Linear user id: the caller's resolved `userId` when it has one, else the viewer query, shared per\n\t * token. A tracker read hands each team the id of the team's own resource, which Linear's team descriptors don't\n\t * name, so without sharing every team read (and a search's fallback) would ask for the viewer again.\n\t */\n\tprivate getViewerId(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: IssuesForProjectOptions,\n\t): Promise<string | undefined> {\n\t\tif (options.userId) return Promise.resolve(options.userId);\n\n\t\treturn this._viewerIds.getOrCreate(\n\t\t\tsession.accessToken,\n\t\t\tasync () => {\n\t\t\t\tconst api = await this.getProvidersApi();\n\t\t\t\treturn (await api.getLinearCurrentUser(toTokenWithInfo(this.id, session)))?.id;\n\t\t\t},\n\t\t\t// Only a resolved id is worth keeping; an unresolved viewer is asked for again next time, as is a failure.\n\t\t\t{ evictWhen: id => id == null },\n\t\t);\n\t}\n\n\t/**\n\t * Follows `getIssues`' cursor up to {@link maxPagesPerRequest}. `truncated` is set when that backstop stopped\n\t * the drain with more pages still available, when the provider stalled its cursor or flagged its paging as\n\t * truncated, or when a page after the first failed, which is recorded at `failureScope` while the prefix is\n\t * kept. A first-page failure throws, so the caller sees a hard error rather than an empty success.\n\t */\n\tprivate async drainIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tfilter: { teams: string[]; assignees?: string[]; states?: GitIssueState[] },\n\t\tsort: IssuesForProjectOptions['sort'],\n\t\tfailureScope: CollectionScopeFailure['scope'],\n\t): Promise<{ issues: IssueShape[]; truncated: boolean; metadata?: CollectionMetadata }> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst assignees = filter.assignees?.length ? filter.assignees : undefined;\n\t\tlet cursor: string | undefined;\n\t\tlet hasMore: boolean;\n\t\tlet requestCount = 0;\n\t\tlet truncated = false;\n\t\tlet collectionMetadata: CollectionMetadata | undefined;\n\t\tconst issues: IssueShape[] = [];\n\t\tdo {\n\t\t\tlet result: Awaited<ReturnType<typeof api.getLinearIssues>>;\n\t\t\ttry {\n\t\t\t\tresult = await api.getLinearIssues(toTokenWithInfo(this.id, session), filter, {\n\t\t\t\t\tcursor: cursor,\n\t\t\t\t\tsort: sort,\n\t\t\t\t});\n\t\t\t} catch (ex) {\n\t\t\t\tif (issues.length === 0) throw ex;\n\n\t\t\t\ttruncated = true;\n\t\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, {\n\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\tfailures: [toCollectionScopeFailure(failureScope, ex)],\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trequestCount += 1;\n\t\t\thasMore = result.paging?.more ?? false;\n\t\t\tconst nextCursor = result.paging?.cursor;\n\t\t\ttruncated ||= result.paging?.truncated === true;\n\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, result.metadata);\n\t\t\tfor (const issue of result.values) {\n\t\t\t\tconst shape = toIssueShape(issue, this, { projection: 'project' });\n\t\t\t\t// The query already scopes to the assignees; checking again keeps another user's issue out of \"my\n\t\t\t\t// issues\" should the provider ever return one.\n\t\t\t\tif (shape != null && (assignees == null || shape.assignees?.some(a => assignees.includes(a.id)))) {\n\t\t\t\t\tissues.push(shape);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The provider claims more but returns no advancing cursor: we can't continue without re-reading the\n\t\t\t// same page, so the drain is incomplete — flag it rather than silently stopping.\n\t\t\tif (hasMore && (nextCursor == null || nextCursor === cursor)) {\n\t\t\t\ttruncated = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcursor = nextCursor;\n\t\t\tif (hasMore && requestCount >= maxPagesPerRequest) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t} while (requestCount < maxPagesPerRequest && hasMore);\n\n\t\treturn { issues: issues, truncated: truncated, metadata: collectionMetadata };\n\t}\n\n\toverride get id(): IssuesCloudHostIntegrationId.Linear {\n\t\treturn IssuesCloudHostIntegrationId.Linear;\n\t}\n\tprotected override get key(): 'linear' {\n\t\treturn 'linear';\n\t}\n\toverride get name(): string {\n\t\treturn metadata.name;\n\t}\n\toverride get domain(): string {\n\t\treturn metadata.domain;\n\t}\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, resources, cancellation))?.values;\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for Linear, drained to exhaustion and reporting whether it got there.\n\t *\n\t * The SDK read is one server-ordered query over an `or` filter across the four relationships (assigned,\n\t * created, named in an issue's content, named in a comment), so each page is 100 issues the caller has not\n\t * seen and the pages are ordered as one sequence. Draining it is therefore just following the cursor.\n\t *\n\t * Overridden rather than left to the base default because that default hardcodes `truncated: false`, and a\n\t * bounded drain that stops early is exactly the case a caller must be able to see. Three things end the\n\t * drain, and only one of them is completion:\n\t *\n\t * - the provider says there is no next page — complete;\n\t * - the backstop is reached — `truncated`, because issues beyond it exist and were not read;\n\t * - the provider claims a next page but hands back a cursor that does not advance — also `truncated`, since\n\t * the read cannot continue and the remainder is unreachable rather than absent.\n\t *\n\t * Cancellation is deliberately NOT truncation: the caller asked for the read to stop, so the partial list is\n\t * what it asked for, and flagging it would surface an incompleteness warning for a user action.\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tif (resources != null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst states = toProviderIssueStates(options?.state);\n\t\tlet cursor = undefined;\n\t\t// Starts false so an immediate cancellation, which leaves the loop before the first response, reads as\n\t\t// \"no more pages known\" rather than as an unfinished drain.\n\t\tlet hasMore = false;\n\t\tlet requestCount = 0;\n\t\tlet truncated = false;\n\t\tconst issues = [];\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tif (cancellation?.aborted) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst result = await api.getIssuesForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\t\t\tcursor: cursor,\n\t\t\t\t\tstates: states,\n\t\t\t\t\tsort: options?.sort,\n\t\t\t\t});\n\t\t\t\trequestCount += 1;\n\t\t\t\thasMore = result.paging?.more ?? false;\n\t\t\t\tconst nextCursor = result.paging?.cursor;\n\n\t\t\t\t// Keep this page before deciding whether to continue: the request is already paid for, so\n\t\t\t\t// dropping its rows would lose real results to save nothing.\n\t\t\t\tconst formattedIssues = result.values\n\t\t\t\t\t.map(issue => toIssueShape(issue, this, { projection: 'account' }))\n\t\t\t\t\t.filter((result): result is IssueShape => result != null);\n\t\t\t\tif (formattedIssues.length > 0) {\n\t\t\t\t\tissues.push(...formattedIssues);\n\t\t\t\t}\n\n\t\t\t\t// The provider claims more but hands back no advancing cursor: continuing would re-read the page\n\t\t\t\t// just returned. `ProvidersApi.getPagedResult` already normalizes this into `more: false`, so\n\t\t\t\t// this is a backstop for if that ever stops holding rather than a case seen today -- but the rest\n\t\t\t\t// of the account is unreachable either way, which is what `truncated` says.\n\t\t\t\tif (hasMore && (nextCursor == null || nextCursor === cursor)) {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcursor = nextCursor;\n\t\t\t} while (requestCount < maxAccountWidePagesPerRequest && hasMore);\n\n\t\t\t// Ran out of budget with pages still to come.\n\t\t\tif (hasMore && requestCount >= maxAccountWidePagesPerRequest) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (issues.length === 0) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\t// Kept what was already fetched, so the list is real but short of the account.\n\t\t\ttruncated = true;\n\t\t\tLogger.error(ex, 'searchProviderMyIssues');\n\t\t}\n\t\treturn { values: issues, truncated: truncated };\n\t}\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\t{ key }: { id: string; key: string },\n\t\t_type: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst issue = await this.getRawProviderIssue(session, resource, key);\n\t\tconst autolinkableIssue: ProviderIssue | undefined = issue && {\n\t\t\t...issue,\n\t\t\turl: this.getIssueAutolinkLikeUrl(issue),\n\t\t};\n\t\treturn autolinkableIssue && toIssueShape(autolinkableIssue, this, { projection: 'point' });\n\t}\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst result = await this.getRawProviderIssue(session, resource, id);\n\t\treturn result && fromProviderIssue(result, this, { projection: 'point' });\n\t}\n\n\tprotected override async getProviderIssueByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresourceId: string,\n\t\tid: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst result = await api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resourceId,\n\t\t\tnumber: id,\n\t\t});\n\t\treturn result && fromProviderIssue(result, this, { projection: 'batch' });\n\t}\n\n\t/**\n\t * The cheap check behind the batch issue read's etags: one `issues` query per team and\n\t * {@link linearIssuesEtagMaxNumbers} distinct numbers, where {@link getProviderIssueByResourceId} sends one request\n\t * per issue. A request that throws rejects only its own targets' slots, with its error classified as the full\n\t * read's would be.\n\t *\n\t * Never proves an absence. The query filters by the team's CURRENT key and asks for archived issues too, but the\n\t * full read's `issue(id:)` may also resolve an identifier the issue no longer carries (its team was renamed, or it\n\t * moved to another team), so an issue the query leaves out may still exist. Its target's slot is rejected, as is a\n\t * target whose identifier isn't a team key and a number, so the full read decides. When no target was answered and\n\t * nothing failed, the check declines instead, so a batch of only such targets costs a full read, not a failure.\n\t */\n\tprotected override async getProviderIssuesEtagFieldsByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined> {\n\t\tconst slots = new Array<PromiseSettledResult<IssueEtagFields | undefined>>(targets.length);\n\t\tlet answered = false;\n\t\tlet failed = false;\n\n\t\t// Each workspace and team's distinct numbers, with the targets asking for each.\n\t\tconst byTeam = new Map<string, { teamKey: string; numbers: Map<number, number[]> }>();\n\t\tfor (const [index, target] of targets.entries()) {\n\t\t\tconst match = linearIdentifier.exec(target.identifier);\n\t\t\tconst number = match != null ? Number(match[2]) : Number.NaN;\n\t\t\t// Only a number written as Linear writes it, so `ENG-012` never answers for `ENG-12`.\n\t\t\tif (match == null || !Number.isSafeInteger(number) || String(number) !== match[2]) {\n\t\t\t\tslots[index] = {\n\t\t\t\t\tstatus: 'rejected',\n\t\t\t\t\treason: new Error(`Not a Linear issue identifier: ${target.identifier}`),\n\t\t\t\t};\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst teamKey = match[1].toUpperCase();\n\t\t\tconst groupKey = `${target.resourceId}\\n${teamKey}`;\n\t\t\tlet group = byTeam.get(groupKey);\n\t\t\tif (group == null) {\n\t\t\t\tgroup = { teamKey: teamKey, numbers: new Map() };\n\t\t\t\tbyTeam.set(groupKey, group);\n\t\t\t}\n\n\t\t\tlet indices = group.numbers.get(number);\n\t\t\tif (indices == null) {\n\t\t\t\tindices = [];\n\t\t\t\tgroup.numbers.set(number, indices);\n\t\t\t}\n\t\t\tindices.push(index);\n\t\t}\n\n\t\tconst requests = [...byTeam.values()].flatMap(({ teamKey, numbers }) =>\n\t\t\tchunk([...numbers], linearIssuesEtagMaxNumbers).map(entries => ({ teamKey: teamKey, entries: entries })),\n\t\t);\n\t\tif (requests.length) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t\tconst answers = await mapSettledBounded(requests, providerFanOutConcurrency, r =>\n\t\t\t\tapi.getLinearIssuesEtagFields(\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\tr.teamKey,\n\t\t\t\t\tr.entries.map(([number]) => number),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tfor (const [i, answer] of answers.entries()) {\n\t\t\t\tconst { teamKey, entries } = requests[i];\n\t\t\t\tif (answer.status === 'rejected') {\n\t\t\t\t\tfailed = true;\n\t\t\t\t\tfor (const [, indices] of entries) {\n\t\t\t\t\t\tfor (const index of indices) {\n\t\t\t\t\t\t\tslots[index] = answer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// By number, never by position: Linear returns the issues in its own order, and only the ones it found.\n\t\t\t\tconst byNumber = new Map<number, LinearIssueEtagNode>();\n\t\t\t\tfor (const issue of answer.value) {\n\t\t\t\t\tbyNumber.set(issue.number, issue);\n\t\t\t\t}\n\n\t\t\t\tfor (const [number, indices] of entries) {\n\t\t\t\t\tconst issue = byNumber.get(number);\n\t\t\t\t\tlet slot: PromiseSettledResult<IssueEtagFields | undefined>;\n\t\t\t\t\tif (issue == null) {\n\t\t\t\t\t\tslot = { status: 'rejected', reason: new Error(`Linear did not return ${teamKey}-${number}`) };\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tslot = { status: 'fulfilled', value: toLinearIssueEtagFields(issue) };\n\t\t\t\t\t\t\tanswered = true;\n\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\tslot = { status: 'rejected', reason: ex };\n\t\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const index of indices) {\n\t\t\t\t\t\tslots[index] = slot;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn answered || failed ? slots : undefined;\n\t}\n\n\tprivate async getRawProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<ProviderIssue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif (!isIssueResourceDescriptor(resource)) {\n\t\t\tLogger.error(undefined, 'getProviderIssue: resource is not an IssueResourceDescriptor');\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resource.id,\n\t\t\tnumber: id,\n\t\t});\n\t}\n\tprivate getIssueAutolinkLikeUrl(issue: ProviderIssue): string | null {\n\t\tconst url = issue.url;\n\t\tif (url == null) return null;\n\n\t\tconst lastSegment = url.split('/').pop();\n\t\tif (!lastSegment || issue.number === lastSegment) {\n\t\t\treturn url;\n\t\t}\n\t\treturn url.substring(0, url.length - lastSegment.length - 1);\n\t}\n}\n"],"mappings":"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"}