@gitkraken/core-gitlens 0.10.2 → 0.10.4

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 (44) 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 +1 -1
  8. package/dist/plus/git-github/api/github.js.map +1 -1
  9. package/dist/plus/integrations/collectionMetadata.d.ts +1 -1
  10. package/dist/plus/integrations/collectionMetadata.js +19 -8
  11. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  12. package/dist/plus/integrations/errors.d.ts +2 -0
  13. package/dist/plus/integrations/errors.js +45 -1
  14. package/dist/plus/integrations/errors.js.map +1 -1
  15. package/dist/plus/integrations/integrationService.js +2 -2
  16. package/dist/plus/integrations/integrationService.js.map +1 -1
  17. package/dist/plus/integrations/models/gitHostIntegration.d.ts +5 -1
  18. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  19. package/dist/plus/integrations/models/integration.d.ts +1 -0
  20. package/dist/plus/integrations/models/integration.js +7 -4
  21. package/dist/plus/integrations/models/integration.js.map +1 -1
  22. package/dist/plus/integrations/providers/azure/azure.js +2 -6
  23. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  24. package/dist/plus/integrations/providers/azureDevOps.d.ts +1 -2
  25. package/dist/plus/integrations/providers/azureDevOps.js +12 -5
  26. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  27. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  28. package/dist/plus/integrations/providers/bitbucket.js +2 -1
  29. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  30. package/dist/plus/integrations/providers/github.d.ts +12 -1
  31. package/dist/plus/integrations/providers/github.js +42 -2
  32. package/dist/plus/integrations/providers/github.js.map +1 -1
  33. package/dist/plus/integrations/providers/gitlab.d.ts +1 -1
  34. package/dist/plus/integrations/providers/gitlab.js +3 -2
  35. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  36. package/dist/plus/integrations/providers/models.js +1 -0
  37. package/dist/plus/integrations/providers/models.js.map +1 -1
  38. package/dist/plus/integrations/results.d.ts +8 -5
  39. package/dist/plus/integrations/results.js +3 -1
  40. package/dist/plus/integrations/results.js.map +1 -1
  41. package/dist/utils/cancellation.d.ts +2 -3
  42. package/dist/utils/cancellation.js +2 -3
  43. package/dist/utils/cancellation.js.map +1 -1
  44. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"azureDevOpsServer.js","names":[],"sources":["../../../../../plus/integrations/src/providers/azureDevOpsServer.ts"],"sourcesContent":["import type { CollectionScopeFailure } from '@gitkraken/provider-apis';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { IssueSearchCriteria } from '@gitlens/git/models/issue.js';\nimport type { PullRequest, PullRequestSearchCriteria, PullRequestSorting } from '@gitlens/git/models/pullRequest.js';\nimport { defaultPullRequestSort } from '@gitlens/git/models/pullRequest.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { uuid } from '@gitlens/utils/crypto.js';\nimport type { Emitter } from '@gitlens/utils/event.js';\nimport { mapBounded } from '@gitlens/utils/promise.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type { ProviderAuthenticationSession, TokenWithInfo } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { GitSelfManagedHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport { IntegrationReadUnavailableError, RequestNotFoundError } from '../errors.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport type {\n\tIntegrationKey,\n\tProviderIssueSearchPage,\n\tProviderPullRequestCount,\n\tProviderPullRequestSearchPage,\n\tProviderSearchCount,\n} from '../models/integration.js';\nimport type { AzureOrganizationDescriptor, AzureProjectDescriptor } from './azure/models.js';\nimport type { AzurePullRequestSearchPosition } from './azure/search.js';\nimport {\n\tcompareAzurePullRequestSearchPositions,\n\tgetInvalidAzureSearchDate,\n\tisAzureWorkItemSearchFirstPage,\n\tparseAzurePullRequestSearchCursor,\n\ttoAzurePullRequestSearchCursorKey,\n\ttoAzurePullRequestSearchFilter,\n\ttoAzurePullRequestSearchPosition,\n\ttoAzureSearchPageSize,\n} from './azure/search.js';\nimport type { AzureReviewerRead } from './azureDevOps.js';\nimport {\n\tAzureDevOpsIntegrationBase,\n\tgetAzureRepositoryApiBaseUrl,\n\tgetAzureRepositoryIdentity,\n\tmarkMyGroupReviews,\n\tsameAzureName,\n\ttoAzureReviewerReads,\n\tuniqueAzureNames,\n} from './azureDevOps.js';\nimport type {\n\tProviderApiCollectionResult,\n\tProviderAzureResource,\n\tProviderPullRequest,\n\tProviderRepoInput,\n\tProviderReposInput,\n} from './models.js';\nimport {\n\tfromProviderPullRequest,\n\tgetProviderPullRequestIdentity,\n\tisNativePullRequest,\n\tprovidersMetadata,\n\tPullRequestFilter,\n\ttoProviderPullRequestStates,\n} from './models.js';\nimport type { ProvidersApi } from './providersApi.js';\n\n/**\n * Whether `value` can stand as one route segment. Encoding keeps `.` and `..` as they are, and a URL resolves them\n * as directory steps, so a name of either would address a different route than the one named.\n */\nfunction isAzureRouteSegment(value: string): boolean {\n\treturn value.length > 0 && value !== '.' && value !== '..';\n}\n\n/**\n * Runs one scope of a count probe, answering a refusal of THAT scope (see `searchUnavailable`) as the error itself,\n * which the facade reports and drops without failing the scopes around it. Anything else — a rejected credential,\n * a failed request — still rejects, so the batch reports it.\n */\nasync function refusalAsCount<T>(count: () => Promise<T>): Promise<T | IntegrationReadUnavailableError> {\n\ttry {\n\t\treturn await count();\n\t} catch (ex) {\n\t\tif (ex instanceof IntegrationReadUnavailableError) return ex;\n\n\t\tthrow ex;\n\t}\n}\n\nfunction findAzureCollection(\n\tcollections: readonly AzureOrganizationDescriptor[],\n\tname: string,\n): AzureOrganizationDescriptor | undefined {\n\treturn collections.find(c => sameAzureName(c.name, name) || c.id === name);\n}\n\n/**\n * The most pull requests one facet of a filtered pull-request search drains. Azure has no server-side text, date or\n * draft filter and no ordering, so a facet is read in full before any of them apply; past this bound the facet's\n * unread rows could sort anywhere, and the search reports itself truncated instead of guessing.\n */\nconst azurePullRequestSearchFacetLimit = 1000;\n\n/**\n * The page size a facet drain reads. The SDK's own default for both pull request reads, and the size it sizes\n * `$skip` by, so the budget above counts what was actually read.\n */\nconst azurePullRequestSearchPageSize = 100;\n\n/**\n * How many scopes of one work-item count batch query at once. The facade already runs several batches concurrently,\n * so this multiplies with it: kept small, so a large count preview can't flood a self-hosted server.\n */\nconst azureIssueCountConcurrency = 2;\n\n/**\n * How long a drained pull-request search keeps serving its continuation pages after the last one was read, and the\n * most it lives however actively it is paged. A pagination that keeps reading stays on one consistent drain; one\n * left idle, or paged for longer than that, is read again.\n */\nconst pullRequestSearchIdleTtl = 5 * 60 * 1000;\nconst pullRequestSearchMaxTtl = 30 * 60 * 1000;\n\n/**\n * How long a count reuses the drain a search of its query just read. Long enough for a count shown next to the\n * search it previews to agree with it, short enough that a count polled next to an unapplied filter stays live: the\n * window runs from the drain, not from the last count, so polling never extends it.\n */\nconst pullRequestCountReuseTtl = 60 * 1000;\n\n/**\n * Kept unconverted, so each page converts its rows with the projection its own caller asked for: the drain is shared\n * by every page of a query, whatever `summary` each page passes.\n */\ninterface AzurePullRequestSearchRow {\n\tpr: ProviderPullRequest;\n\tproject: AzureProjectDescriptor;\n\tcurrentAccount: { id: string } | undefined;\n\tposition: AzurePullRequestSearchPosition;\n}\n\ninterface AzurePullRequestSearchDrain {\n\trows: AzurePullRequestSearchRow[];\n\ttruncated: boolean;\n\t/** Collections whose groups couldn't be read, so what only those groups review is missing. */\n\tfailures?: CollectionScopeFailure[];\n}\n\n/**\n * Everything that decides a pull-request search's result set and order, as the value its cursor is fingerprinted\n * from. `pageSize` is left out on purpose: it only decides where a page ends, which the keyset cursor already\n * carries.\n */\nfunction toPullRequestSearchQuery(\n\toptions: { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria },\n\tsort: PullRequestSorting,\n): unknown {\n\tconst criteria = options.criteria;\n\treturn {\n\t\trepos: options.repos?.map(r => [r.namespace, r.project ?? null, r.name]),\n\t\torg: options.org ?? null,\n\t\trelationships: [...new Set(criteria?.relationships ?? [])].sort(),\n\t\tstates: [...new Set(criteria?.states ?? [])].sort(),\n\t\ttext: criteria?.text?.trim() ?? null,\n\t\tdraft: criteria?.draft ?? null,\n\t\tupdatedAfter: criteria?.updatedAfter ?? null,\n\t\tcreatedAfter: criteria?.createdAfter ?? null,\n\t\tsort: sort,\n\t};\n}\n\nconst serverMetadata = providersMetadata[GitSelfManagedHostIntegrationId.AzureDevOpsServer];\nconst serverAuthProvider = Object.freeze({ id: serverMetadata.id, scopes: serverMetadata.scopes });\n\nexport class AzureDevOpsServerIntegration extends AzureDevOpsIntegrationBase<GitSelfManagedHostIntegrationId.AzureDevOpsServer> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = serverAuthProvider;\n\treadonly id = GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\tprotected readonly key: IntegrationKey<GitSelfManagedHostIntegrationId.AzureDevOpsServer>;\n\treadonly name: string = 'Azure DevOps Server';\n\n\tconstructor(\n\t\tctx: IntegrationServiceContext,\n\t\tauthenticationService: IntegrationAuthenticationService,\n\t\tgetProvidersApi: () => Promise<ProvidersApi>,\n\t\tdidChangeConnection: Emitter<IntegrationConnectionChangeEvent>,\n\t\treadonly domain: string,\n\t) {\n\t\tsuper(ctx, authenticationService, getProvidersApi, didChangeConnection);\n\t\tthis.key = `${this.id}:${this.domain}`;\n\t}\n\n\tprotected override apiBaseUrlFor(session: ProviderAuthenticationSession): string {\n\t\treturn this.getSelfManagedApiBaseUrl(session);\n\t}\n\n\t/**\n\t * The credential AND the installation it is used against: one integration instance serves every installation on\n\t * its host (context paths differ, the host doesn't), and the same token connected to two of them must not reuse\n\t * one installation's collections or projects on the other.\n\t */\n\tprotected override discoveryKey(session: ProviderAuthenticationSession): string {\n\t\treturn `${session.accessToken}:${this.apiBaseUrlFor(session)}`;\n\t}\n\n\tprotected override getRepositoriesApiBaseUrl(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos: ProviderReposInput,\n\t): string {\n\t\t// Requests append each repository's organization (its collection), which an address naming that collection\n\t\t// already ends with. Only repositories of a single organization can share one base; mixed ones keep the\n\t\t// address, so another collection's repository stays below it (see `resolveRepository`).\n\t\tconst owners = new Set(repos.map(r => (typeof r === 'object' ? r.namespace : undefined)));\n\t\tconst [owner] = owners;\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\t\treturn owners.size === 1 && owner != null\n\t\t\t? getAzureRepositoryApiBaseUrl(baseUrl, { owner: owner, virtualDirectory: undefined })\n\t\t\t: baseUrl;\n\t}\n\n\tprotected override getApiOptions(\n\t\tsession: ProviderAuthenticationSession,\n\t\tdoNotConvertToPat: boolean = false,\n\t): {\n\t\ttokenWithInfo: TokenWithInfo<GitSelfManagedHostIntegrationId.AzureDevOpsServer>;\n\t\toptions: { isPAT: boolean; baseUrl?: string };\n\t} {\n\t\tconst { options, ...rest } = super.getApiOptions(session, doNotConvertToPat);\n\t\treturn {\n\t\t\t...rest,\n\t\t\toptions: { ...options, baseUrl: this.apiBaseUrlFor(session) },\n\t\t};\n\t}\n\n\tprotected override getCollectionApiOptions(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcollection: string,\n\t): { isPAT: boolean; baseUrl?: string } {\n\t\treturn {\n\t\t\t...super.getCollectionApiOptions(session, collection),\n\t\t\tbaseUrl: this.collectionApiBaseUrl(session, collection),\n\t\t};\n\t}\n\n\t/** The base a request below `collection` appends to: the installation, whichever the address already ends in. */\n\tprotected override collectionApiBaseUrl(session: ProviderAuthenticationSession, collection: string): string {\n\t\treturn getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), {\n\t\t\towner: collection,\n\t\t\tvirtualDirectory: undefined,\n\t\t});\n\t}\n\n\t/**\n\t * Only the server level lists the collections: below one, Azure DevOps Server answers `projectCollections` with a\n\t * 404 page. On that 404 an address that names a collection reads the collection's own connection data, whose\n\t * `webApplicationRelativeDirectory` spells it, and reports it as the one collection visible there.\n\t */\n\tprotected override async requestResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tuserId: string,\n\t): Promise<ProviderAzureResource[] | undefined> {\n\t\ttry {\n\t\t\treturn await super.requestResourcesForUser(session, userId);\n\t\t} catch (ex) {\n\t\t\tif (!(ex instanceof RequestNotFoundError)) throw ex;\n\n\t\t\tconst azure = await this.authenticationService.apis.azure;\n\t\t\tconst addressed = await azure?.getAddressedCollection(\n\t\t\t\tthis,\n\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\tthis.apiBaseUrlFor(session),\n\t\t\t);\n\t\t\tif (addressed == null) throw ex;\n\n\t\t\treturn [addressed];\n\t\t}\n\t}\n\n\t/**\n\t * The filtered work-item search: one WIQL query per page over ONE collection, then one detail read for the\n\t * page's slice. See {@link resolveWorkItemSearchScope} for why a search never spans several collections.\n\t */\n\tprotected override async searchProviderIssuesPage(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: {\n\t\t\trepos?: ProviderRepoInput[];\n\t\t\torg?: string;\n\t\t\tcriteria?: IssueSearchCriteria;\n\t\t\tcursor?: string;\n\t\t\tpageSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<ProviderIssueSearchPage | undefined> {\n\t\tconst azure = await this.authenticationService.apis.azure;\n\t\tif (azure == null) return undefined;\n\n\t\tthis.assertSearchDates(options.criteria);\n\t\t// A first page re-reads the collection's projects, so a project created since discovery is known before its\n\t\t// work items can turn up in a collection-wide query; a continuation keeps the set its first page resolved.\n\t\t// The facade's page-1 marker (`broadenIssues` retries a failed first page with it) is a first page too.\n\t\tconst scope = await this.resolveWorkItemSearchScope(\n\t\t\tsession,\n\t\t\toptions.repos,\n\t\t\toptions.org,\n\t\t\tisAzureWorkItemSearchFirstPage(options.cursor),\n\t\t);\n\t\treturn azure.searchWorkItemsPage(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tscope.collection,\n\t\t\t{\n\t\t\t\tbaseUrl: this.collectionApiBaseUrl(session, scope.collection),\n\t\t\t\tcriteria: options.criteria,\n\t\t\t\tprojectNames: scope.projectNames,\n\t\t\t\tresolveProject: name => scope.projects.find(p => sameAzureName(p.name, name)),\n\t\t\t\tcursor: options.cursor,\n\t\t\t\tpageSize: options.pageSize,\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\t/**\n\t * Counts each scope with the WIQL its search would page through, so a count never previews constraints the read\n\t * doesn't apply. A scope this provider refuses (see {@link resolveWorkItemSearchScope}) is answered as that\n\t * refusal, so the facade drops only it; a request failure still fails the batch, which the facade isolates from\n\t * the other batches.\n\t */\n\tprotected override async countProviderIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tscopes: readonly { repos?: ProviderRepoInput[]; org?: string; criteria?: IssueSearchCriteria }[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<ProviderSearchCount[] | undefined> {\n\t\tconst azure = await this.authenticationService.apis.azure;\n\t\tif (azure == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\treturn mapBounded(scopes, azureIssueCountConcurrency, s =>\n\t\t\trefusalAsCount(async () => {\n\t\t\t\tthis.assertSearchDates(s.criteria);\n\t\t\t\t// Re-reads the projects like a first search page does, so a count and the page it previews resolve\n\t\t\t\t// the same project set — a newer project can't be counted by one and refused or missed by the other.\n\t\t\t\tconst scope = await this.resolveWorkItemSearchScope(session, s.repos, s.org, true);\n\t\t\t\treturn azure.countWorkItems(\n\t\t\t\t\tthis,\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\tscope.collection,\n\t\t\t\t\t{\n\t\t\t\t\t\tbaseUrl: this.collectionApiBaseUrl(session, scope.collection),\n\t\t\t\t\t\tcriteria: s.criteria,\n\t\t\t\t\t\tprojectNames: scope.projectNames,\n\t\t\t\t\t},\n\t\t\t\t\tcancellation,\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * The ONE collection a work-item search runs in, and the projects that bound it.\n\t *\n\t * A WIQL query is collection-scoped, and ordering a union of several would need every match's details rather\n\t * than one page's, so a search never spans collections. `org` names the collection; without it the repositories'\n\t * collection is used, and without either the only collection the account can see. Anything else — repositories\n\t * in two collections, or several visible collections and nothing to choose one — is refused rather than\n\t * narrowed to whichever came first.\n\t *\n\t * Repositories bound the query by their PROJECT, since Azure work items belong to projects rather than to\n\t * repositories, and every project must be one the collection actually has: WIQL answers an unknown project name\n\t * with an empty result, which would read as \"nothing matched\".\n\t */\n\tprivate async resolveWorkItemSearchScope(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos: ProviderRepoInput[] | undefined,\n\t\torg: string | undefined,\n\t\trefreshProjects: boolean = false,\n\t): Promise<{ collection: string; projects: AzureProjectDescriptor[]; projectNames?: string[] }> {\n\t\tconst repoCollections = uniqueAzureNames(repos?.map(r => r.namespace) ?? []);\n\t\tif (repoCollections.length > 1) {\n\t\t\tthrow this.searchUnavailable(\n\t\t\t\t'the repositories span several collections; search each collection separately',\n\t\t\t);\n\t\t}\n\t\tif (org && repoCollections.length === 1 && !sameAzureName(org, repoCollections[0])) {\n\t\t\tthrow this.searchUnavailable(`the repositories are not in the '${org}' collection`);\n\t\t}\n\n\t\tconst collections = await this.getSearchCollections(session);\n\t\tconst name = org || repoCollections[0] || (collections.length === 1 ? collections[0].name : undefined);\n\t\tif (name == null) {\n\t\t\tthrow this.searchUnavailable(\n\t\t\t\t`the account can see ${collections.length} collections; pass \\`org\\` to choose the one to search`,\n\t\t\t);\n\t\t}\n\n\t\tconst collection = findAzureCollection(collections, name);\n\t\tif (collection == null) throw this.searchUnavailable(`the '${name}' collection is not visible`);\n\n\t\tconst projects = await this.getSearchProjects(session, collection, refreshProjects);\n\t\tif (!repos?.length) return { collection: collection.name, projects: projects };\n\n\t\tconst projectNames = uniqueAzureNames(\n\t\t\trepos.map(r => {\n\t\t\t\tconst projectName = getAzureRepositoryIdentity({\n\t\t\t\t\towner: r.namespace,\n\t\t\t\t\tname: r.name,\n\t\t\t\t\tproject: r.project,\n\t\t\t\t}).projectName;\n\t\t\t\tif (projectName == null) {\n\t\t\t\t\tthrow this.searchUnavailable(`repository '${r.namespace}/${r.name}' names no project`);\n\t\t\t\t}\n\n\t\t\t\tconst project = projects.find(p => sameAzureName(p.name, projectName));\n\t\t\t\tif (project == null) {\n\t\t\t\t\tthrow this.searchUnavailable(`project '${projectName}' is not visible in '${collection.name}'`);\n\t\t\t\t}\n\n\t\t\t\treturn project.name;\n\t\t\t}),\n\t\t);\n\t\treturn { collection: collection.name, projects: projects, projectNames: projectNames };\n\t}\n\n\t/**\n\t * The filtered pull-request search. Azure's pull request query can only narrow by creator, reviewer, status and\n\t * repository, so every facet is drained (see {@link drainPullRequestSearch}) and the criteria it can't express\n\t * are applied to the complete facets; the union is then ordered and served from a keyset cursor.\n\t */\n\tprotected override async searchProviderPullRequestsPage(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: {\n\t\t\trepos?: ProviderRepoInput[];\n\t\t\torg?: string;\n\t\t\tcriteria?: PullRequestSearchCriteria;\n\t\t\tcursor?: string;\n\t\t\tpageSize?: number;\n\t\t\tsummary?: boolean;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<ProviderPullRequestSearchPage | undefined> {\n\t\tthis.assertSearchDates(options.criteria);\n\t\tconst sort = options.criteria?.sort ?? defaultPullRequestSort;\n\t\tconst query = toPullRequestSearchQuery(options, sort);\n\t\tconst key = toAzurePullRequestSearchCursorKey(query);\n\t\tconst cursor = parseAzurePullRequestSearchCursor(options.cursor, key);\n\t\t// A first page drains fresh, into a drain of its own, and its continuations page through that one: the cache\n\t\t// keeps it alive for as long as the pagination keeps reading it (an access TTL), so rows can't move between\n\t\t// pages, and a later first page of the same query never replaces it. A continuation whose drain is gone is\n\t\t// refused, like an expired work-item snapshot: resuming against a re-drain could skip or repeat a pull request\n\t\t// whose sort date changed. The drain is keyed by credential and installation too, so a cursor handed to\n\t\t// another connection finds nothing and is refused the same way.\n\t\tconst drainId = cursor?.drain ?? uuid();\n\t\tconst drainKey = this.pullRequestSearchCacheKey(session, query, drainId);\n\t\t// A continuation only ever READS its drain: creating one under its id would be the re-drain refused above.\n\t\tconst drainFound =\n\t\t\tcursor != null\n\t\t\t\t? this._pullRequestSearches.get(drainKey)\n\t\t\t\t: this.getPullRequestSearch(this._pullRequestSearches, drainKey, session, options, sort, cancellation);\n\t\tif (drainFound == null) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pull request search results expired or belong to another connection; restart the read without a cursor',\n\t\t\t);\n\t\t}\n\n\t\tconst drain = await drainFound;\n\t\tif (cursor == null) {\n\t\t\t// Also the query's latest drain, which a count of the same query reads rather than draining again. Stored\n\t\t\t// through `getOrCreate`, which applies the cache's capacity and expiry; `set` applies neither.\n\t\t\tconst latestKey = this.pullRequestSearchCacheKey(session, query);\n\t\t\tthis._latestPullRequestSearches.delete(latestKey);\n\t\t\tvoid this._latestPullRequestSearches.getOrCreate(latestKey, () => Promise.resolve(drain));\n\t\t}\n\n\t\tconst pageSize = toAzureSearchPageSize(options.pageSize, 50, 100);\n\t\tconst start =\n\t\t\tcursor == null\n\t\t\t\t? 0\n\t\t\t\t: drain.rows.findIndex(r => compareAzurePullRequestSearchPositions(r.position, cursor.after, sort) > 0);\n\t\tconst slice = start < 0 ? [] : drain.rows.slice(start, start + pageSize);\n\t\tconst page = cursor?.page ?? 1;\n\t\tconst last = slice.at(-1);\n\t\tconst hasMore = last != null && start + slice.length < drain.rows.length;\n\t\treturn {\n\t\t\tvalues: slice.map(r =>\n\t\t\t\tfromProviderPullRequest(r.pr, this, {\n\t\t\t\t\tproject: r.project,\n\t\t\t\t\tcurrentAccount: r.currentAccount,\n\t\t\t\t\tprojection: options.summary ? 'search-summary' : 'search',\n\t\t\t\t}),\n\t\t\t),\n\t\t\tcursor: hasMore\n\t\t\t\t? JSON.stringify({ key: key, drain: drainId, page: page + 1, after: last.position })\n\t\t\t\t: undefined,\n\t\t\thasMore: hasMore,\n\t\t\tpage: page,\n\t\t\ttruncated: drain.truncated,\n\t\t\ttotalCount: drain.truncated ? undefined : drain.rows.length,\n\t\t\tmetadata: drain.failures != null ? { completeness: 'partial', failures: drain.failures } : undefined,\n\t\t};\n\t}\n\n\t/**\n\t * Counts each scope from the latest drain of its query, the one a search's first page just read, or drains it; see\n\t * {@link searchProviderPullRequestsPage}.\n\t */\n\tprotected override async countProviderPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\tscopes: readonly { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria }[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<(ProviderPullRequestCount | Error)[] | undefined> {\n\t\t// One scope at a time: each is a drain that already fans out over its facets, and the facade runs several\n\t\t// batches of scopes at once, so a concurrent batch would multiply the requests reaching the server.\n\t\treturn mapBounded(scopes, 1, s =>\n\t\t\trefusalAsCount(async () => {\n\t\t\t\tthis.assertSearchDates(s.criteria);\n\t\t\t\tconst sort = s.criteria?.sort ?? defaultPullRequestSort;\n\t\t\t\tconst drain = await this.getPullRequestSearch(\n\t\t\t\t\tthis._latestPullRequestSearches,\n\t\t\t\t\tthis.pullRequestSearchCacheKey(session, toPullRequestSearchQuery(s, sort)),\n\t\t\t\t\tsession,\n\t\t\t\t\ts,\n\t\t\t\t\tsort,\n\t\t\t\t\tcancellation,\n\t\t\t\t);\n\t\t\t\t// Past the drain budget the true total is unknown, so it is reported as uncounted rather than understated.\n\t\t\t\treturn { count: drain.truncated ? undefined : drain.rows.length };\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * The drain each pagination pages through, one per first page (see {@link searchProviderPullRequestsPage}), so\n\t * the 50 most recently read paginations are kept for as long as they keep reading.\n\t */\n\tprivate readonly _pullRequestSearches = new PromiseCache<string, AzurePullRequestSearchDrain>({\n\t\tcapacity: 50,\n\t\taccessTTL: pullRequestSearchIdleTtl,\n\t\tcreateTTL: pullRequestSearchMaxTtl,\n\t});\n\t/**\n\t * Each query's latest drain, which its count reads for {@link pullRequestCountReuseTtl}. Apart from the\n\t * paginations' drains, so counting and starting searches never evicts a pagination still being read.\n\t */\n\tprivate readonly _latestPullRequestSearches = new PromiseCache<string, AzurePullRequestSearchDrain>({\n\t\tcapacity: 20,\n\t\tcreateTTL: pullRequestCountReuseTtl,\n\t});\n\n\t/**\n\t * Keyed by the query itself, not its cursor fingerprint, so two queries whose fingerprints collide can never share\n\t * a drain; by credential and installation, so two accounts or installations never read each other's results; and\n\t * by `drainId` for a pagination's own drain, left out for the query's latest one.\n\t */\n\tprivate pullRequestSearchCacheKey(\n\t\tsession: ProviderAuthenticationSession,\n\t\tquery: unknown,\n\t\tdrainId?: string,\n\t): string {\n\t\treturn JSON.stringify([session.accessToken, this.apiBaseUrlFor(session), query, drainId ?? null]);\n\t}\n\n\tprivate getPullRequestSearch(\n\t\tcache: PromiseCache<string, AzurePullRequestSearchDrain>,\n\t\tcacheKey: string,\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria },\n\t\tsort: PullRequestSorting,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<AzurePullRequestSearchDrain> {\n\t\treturn cache.getOrCreate(\n\t\t\tcacheKey,\n\t\t\t(_cacheable, signal) => this.drainPullRequestSearch(session, options, sort, signal),\n\t\t\t{ cancellation: cancellation },\n\t\t);\n\t}\n\n\t/**\n\t * Every pull request matching the search, filtered, deduplicated and ordered.\n\t *\n\t * One facet per (repository or project) × relationship: `Author` reads by creator, while `Assignee` and\n\t * `ReviewRequested` both read by reviewer, since Azure has no assignee distinct from its reviewers: every open pull\n\t * request, kept when it names the user or one of the user's groups, and closed or merged ones through Azure's filter\n\t * for the user (see `toAzureReviewerReads`). Each facet is drained to {@link azurePullRequestSearchFacetLimit}; a\n\t * facet that still has more marks the whole result truncated, because its unread rows could sort anywhere in the\n\t * union. A facet that fails fails the search: a union missing one facet would be served in an order it doesn't\n\t * have. A collection whose groups can't be read is the exception: its facets still read what names the user, and\n\t * the result is reported incomplete with that collection's failure.\n\t */\n\tprivate async drainPullRequestSearch(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria },\n\t\tsort: PullRequestSorting,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<AzurePullRequestSearchDrain> {\n\t\tconst criteria = options.criteria;\n\t\t// Built before any request, so criteria it can't apply fail the search before the facets are drained.\n\t\tconst matches = toAzurePullRequestSearchFilter(criteria);\n\t\tconst relationships = new Set(criteria?.relationships ?? []);\n\t\tconst wantAuthored = relationships.has(PullRequestFilter.Author);\n\t\tconst wantReviewed =\n\t\t\trelationships.has(PullRequestFilter.Assignee) || relationships.has(PullRequestFilter.ReviewRequested);\n\n\t\t// A drain runs for a first page (or once its predecessor expired), so it re-reads the projects it fans out\n\t\t// over: an org search trusting the connection-long discovery cache would silently skip a newer project.\n\t\tconst facets = await this.resolvePullRequestSearchFacets(session, options.repos, options.org);\n\n\t\t// The current user's id is resolved PER COLLECTION: Azure DevOps Server gives one person a different\n\t\t// identity id in each collection than at the server level, and a creator/reviewer filter only matches the\n\t\t// collection's. The server-level id matches nothing there and would serve an empty result as if nobody had\n\t\t// authored anything.\n\t\tconst userIds = new Map<string, string>();\n\t\tif (relationships.size > 0) {\n\t\t\tfor (const collection of uniqueAzureNames(facets.map(f => f.collection))) {\n\t\t\t\tconst userId = await this.getFilterUserId(session, collection);\n\t\t\t\tif (userId == null) {\n\t\t\t\t\tthrow this.searchUnavailable(`the current user could not be resolved in '${collection}'`);\n\t\t\t\t}\n\n\t\t\t\tuserIds.set(collection, userId);\n\t\t\t}\n\t\t}\n\n\t\t// Every group the user is in, per collection, which the reviewer facets keep pull requests for too (see\n\t\t// `toAzureReviewerReads`). A collection whose groups can't be read (e.g. a token without the Identity scope)\n\t\t// keeps what names the user and is reported as a failure of that collection, so the search is incomplete\n\t\t// rather than refused, and a credential its other reads accept isn't treated as a dead one.\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tconst groupsByCollection = new Map(\n\t\t\twantReviewed\n\t\t\t\t? await Promise.all(\n\t\t\t\t\t\tArray.from(userIds, async ([collection, userId]) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\t\t\tawait this.getReviewerGroupIds(session, collection, userId),\n\t\t\t\t\t\t\t\t] as const;\n\t\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\t\tconst resourceId = facets.find(f => f.collection === collection)!.project.resourceId;\n\t\t\t\t\t\t\t\tfailures.push(\n\t\t\t\t\t\t\t\t\ttoCollectionScopeFailure({ providerId: this.id, resourceId: resourceId }, ex),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn [collection, new Set<string>()] as const;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t: [],\n\t\t);\n\t\tconst groupIds = new Set([...groupsByCollection.values()].flatMap(ids => [...ids]));\n\n\t\tconst states = toProviderPullRequestStates(criteria?.states?.length ? criteria.states : 'open');\n\t\tconst filtersFor = (\n\t\t\tcollection: string,\n\t\t): { filter: AzureReviewerRead['filter'] & { authorLogin?: string }; keep?: AzureReviewerRead['keep'] }[] => {\n\t\t\tif (relationships.size === 0) return [{ filter: {} }];\n\n\t\t\tconst userId = userIds.get(collection)!;\n\t\t\treturn [\n\t\t\t\t...(wantAuthored ? [{ filter: { authorLogin: userId } }] : []),\n\t\t\t\t...(wantReviewed ? toAzureReviewerReads(userId, groupsByCollection.get(collection)!, states) : []),\n\t\t\t];\n\t\t};\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);\n\t\tconst drains = await mapBounded(\n\t\t\tfacets.flatMap(facet => filtersFor(facet.collection).map(f => ({ facet: facet, ...f }))),\n\t\t\tproviderFanOutConcurrency,\n\t\t\tasync ({ facet, filter, keep }) => {\n\t\t\t\tconst baseUrl = this.collectionApiBaseUrl(session, facet.collection);\n\t\t\t\tconst values: ProviderPullRequest[] = [];\n\t\t\t\tlet page = 1;\n\t\t\t\tfor (let read = 1; ; read++) {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\tconst input = {\n\t\t\t\t\t\t...apiOptions,\n\t\t\t\t\t\t// A reviewer facet narrows the states itself (see `toAzureReviewerReads`).\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t\t...filter,\n\t\t\t\t\t\tpage: page,\n\t\t\t\t\t\tpageSize: azurePullRequestSearchPageSize,\n\t\t\t\t\t\tbaseUrl: baseUrl,\n\t\t\t\t\t};\n\t\t\t\t\tconst result =\n\t\t\t\t\t\tfacet.repository != null\n\t\t\t\t\t\t\t? await api\n\t\t\t\t\t\t\t\t\t.getPullRequestsForRepo(\n\t\t\t\t\t\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tnamespace: facet.collection,\n\t\t\t\t\t\t\t\t\t\t\tname: facet.repository,\n\t\t\t\t\t\t\t\t\t\t\tproject: facet.project.name,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tinput,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t.then(r => ({\n\t\t\t\t\t\t\t\t\t\tvalues: r.values,\n\t\t\t\t\t\t\t\t\t\tmore: r.paging?.more === true,\n\t\t\t\t\t\t\t\t\t\tnextPage: r.paging?.nextPage,\n\t\t\t\t\t\t\t\t\t}))\n\t\t\t\t\t\t\t: await api\n\t\t\t\t\t\t\t\t\t.getPullRequestsForAzureProject(\n\t\t\t\t\t\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t\t\t\t\t\t{ namespace: facet.collection, project: facet.project.name },\n\t\t\t\t\t\t\t\t\t\tinput,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t.then(r =>\n\t\t\t\t\t\t\t\t\t\tr != null\n\t\t\t\t\t\t\t\t\t\t\t? { values: r.data, more: r.hasMore, nextPage: r.nextPage ?? undefined }\n\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\tif (result == null) throw new Error('Azure DevOps returned no pull request page');\n\n\t\t\t\t\tvalues.push(...result.values);\n\t\t\t\t\tif (!result.more) return { facet: facet, keep: keep, values: values, truncated: false };\n\t\t\t\t\t// Follows the page the provider says comes next rather than assuming one: a continuation that\n\t\t\t\t\t// doesn't advance would re-read or skip rows, so it fails the facet — and with it the search —\n\t\t\t\t\t// instead of serving a union built from it.\n\t\t\t\t\tif (result.nextPage == null || result.nextPage <= page) {\n\t\t\t\t\t\tthrow new Error('Azure DevOps returned no advancing pull request continuation');\n\t\t\t\t\t}\n\t\t\t\t\t// The budget counts pages READ, not rows kept: several states are one `status=all` query the SDK\n\t\t\t\t\t// narrows client-side, so counting survivors would keep paging through rows it discards.\n\t\t\t\t\tif (read * azurePullRequestSearchPageSize >= azurePullRequestSearchFacetLimit) {\n\t\t\t\t\t\treturn { facet: facet, keep: keep, values: values, truncated: true };\n\t\t\t\t\t}\n\n\t\t\t\t\tpage = result.nextPage;\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tconst rows = new Map<string, AzurePullRequestSearchRow>();\n\t\tfor (const { facet, keep, values } of drains) {\n\t\t\tfor (const pr of values) {\n\t\t\t\tif (keep != null && !keep(pr)) continue;\n\t\t\t\tif (!matches(pr)) continue;\n\n\t\t\t\tconst identity =\n\t\t\t\t\tgetProviderPullRequestIdentity(pr) ?? `project:${facet.collection}/${facet.project.name}:${pr.id}`;\n\t\t\t\tif (rows.has(identity)) continue;\n\n\t\t\t\tconst userId = userIds.get(facet.collection);\n\t\t\t\trows.set(identity, {\n\t\t\t\t\tpr: markMyGroupReviews(pr, groupIds),\n\t\t\t\t\tproject: facet.project,\n\t\t\t\t\tcurrentAccount: userId != null ? { id: userId } : undefined,\n\t\t\t\t\tposition: toAzurePullRequestSearchPosition(pr, sort, identity),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\trows: [...rows.values()].sort((a, b) =>\n\t\t\t\tcompareAzurePullRequestSearchPositions(a.position, b.position, sort),\n\t\t\t),\n\t\t\ttruncated: failures.length > 0 || drains.some(d => d.truncated),\n\t\t\tfailures: failures.length > 0 ? failures : undefined,\n\t\t};\n\t}\n\n\t/**\n\t * The repositories or projects a pull-request search reads, each with its collection and project descriptor.\n\t *\n\t * Repositories are read one by one; an `org` is its collection's projects; a search bounded only by the current\n\t * user reads every project the account can see, the same breadth as the account-wide pull request read.\n\t */\n\tprivate async resolvePullRequestSearchFacets(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos: ProviderRepoInput[] | undefined,\n\t\torg: string | undefined,\n\t): Promise<{ collection: string; project: AzureProjectDescriptor; repository?: string }[]> {\n\t\tconst collections = await this.getSearchCollections(session);\n\t\tconst collectionFor = (name: string): AzureOrganizationDescriptor => {\n\t\t\tconst collection = findAzureCollection(collections, name);\n\t\t\tif (collection == null) throw this.searchUnavailable(`the '${name}' collection is not visible`);\n\n\t\t\treturn collection;\n\t\t};\n\n\t\tif (repos?.length) {\n\t\t\tif (org && repos.some(r => !sameAzureName(r.namespace, org))) {\n\t\t\t\tthrow this.searchUnavailable(`the repositories are not all in the '${org}' collection`);\n\t\t\t}\n\n\t\t\t// One discovery per distinct collection, however many of its repositories the search names.\n\t\t\tconst projectsByCollection = new Map<string, Promise<AzureProjectDescriptor[]>>();\n\t\t\tconst projectsOf = (collection: AzureOrganizationDescriptor): Promise<AzureProjectDescriptor[]> => {\n\t\t\t\tlet projects = projectsByCollection.get(collection.id);\n\t\t\t\tif (projects == null) {\n\t\t\t\t\tprojects = this.getSearchProjects(session, collection, true);\n\t\t\t\t\tprojectsByCollection.set(collection.id, projects);\n\t\t\t\t}\n\t\t\t\treturn projects;\n\t\t\t};\n\n\t\t\treturn Promise.all(\n\t\t\t\trepos.map(async r => {\n\t\t\t\t\tconst collection = collectionFor(r.namespace);\n\t\t\t\t\tconst identity = getAzureRepositoryIdentity({\n\t\t\t\t\t\towner: r.namespace,\n\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\tproject: r.project,\n\t\t\t\t\t});\n\t\t\t\t\tif (identity.projectName == null) {\n\t\t\t\t\t\tthrow this.searchUnavailable(`repository '${r.namespace}/${r.name}' names no project`);\n\t\t\t\t\t}\n\t\t\t\t\t// The repository becomes a URL segment the SDK encodes but doesn't validate, and a segment of `..`\n\t\t\t\t\t// survives encoding and resolves to the project-wide route: refused rather than widened.\n\t\t\t\t\tif (!isAzureRouteSegment(identity.repositoryName)) {\n\t\t\t\t\t\tthrow this.searchUnavailable(`repository '${r.namespace}/${r.name}' is not a valid name`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst project = (await projectsOf(collection)).find(p =>\n\t\t\t\t\t\tsameAzureName(p.name, identity.projectName!),\n\t\t\t\t\t);\n\t\t\t\t\tif (project == null) {\n\t\t\t\t\t\tthrow this.searchUnavailable(\n\t\t\t\t\t\t\t`project '${identity.projectName}' is not visible in '${collection.name}'`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { collection: collection.name, project: project, repository: identity.repositoryName };\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst scoped = org ? [collectionFor(org)] : collections;\n\t\tconst projects = await Promise.all(scoped.map(c => this.getSearchProjects(session, c, true)));\n\t\treturn projects.flat().map(p => ({ collection: p.resourceName, project: p }));\n\t}\n\n\tprivate readonly _collectionUserIds = new PromiseCache<string, string | undefined>({ capacity: 50 });\n\n\t/**\n\t * The current user's identity id in `collection`, read from the collection's own connection data, which is\n\t * what Azure's creator and reviewer filters compare against. Cached per credential and collection, since it\n\t * can't change for either.\n\t */\n\tprotected override getFilterUserId(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcollection: string,\n\t): Promise<string | undefined> {\n\t\tconst baseUrl = this.collectionApiBaseUrl(session, collection);\n\t\treturn this._collectionUserIds.getOrCreate(\n\t\t\tJSON.stringify([session.accessToken, baseUrl, collection.toLowerCase()]),\n\t\t\tasync cacheable => {\n\t\t\t\tconst azure = await this.authenticationService.apis.azure;\n\t\t\t\tconst user = await azure?.getCurrentUserOnServer(\n\t\t\t\t\tthis,\n\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\t`${baseUrl}/${encodeURIComponent(collection)}`,\n\t\t\t\t);\n\t\t\t\t// A miss is not cached: the lookup degrades to `undefined` on a transient failure, and caching that\n\t\t\t\t// would refuse every relationship search in the collection until the connection is dropped.\n\t\t\t\tif (user?.id == null) {\n\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t}\n\n\t\t\t\treturn user?.id;\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Resolved per collection, which a raw row names as its repository owner: a collection's pull requests carry the\n\t * id {@link getFilterUserId} resolves there, never the server-level account's. A row that names no collection or\n\t * has no author to match gets no viewer, nor does one in a collection whose id can't be read: its authorship\n\t * stays unknown.\n\t */\n\toverride async getPullRequestViewers(\n\t\tpullRequests: readonly (ProviderPullRequest | PullRequest)[],\n\t\tconnectionId?: string,\n\t): Promise<({ id: string; username?: string } | undefined)[]> {\n\t\tconst session = await this.resolveReadSessionOrThrow(connectionId, undefined);\n\t\tif (session == null) return pullRequests.map(() => undefined);\n\n\t\tconst viewerFor = this.getOrganizationViewers(session, undefined);\n\t\treturn Promise.all(\n\t\t\tpullRequests.map(pr => {\n\t\t\t\t// Azure DevOps Server's rows are provider-apis' own; only GitHub's are native.\n\t\t\t\tconst collection = isNativePullRequest(pr) ? undefined : pr.repository?.owner?.login;\n\t\t\t\treturn collection && pr.author != null ? viewerFor(collection) : Promise.resolve(undefined);\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Ignores `account`: it is the server-level one, which no collection's pull requests carry. A collection whose id\n\t * can't be read resolves `undefined`, since authorship is optional enrichment and one collection's failed lookup\n\t * must not fail the read or blank the others; it is asked once per read, as a miss isn't cached.\n\t */\n\tprotected override getOrganizationViewers(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_account: { id: string; username?: string } | undefined,\n\t): (collection: string) => Promise<{ id: string } | undefined> {\n\t\tconst viewers = new Map<string, Promise<{ id: string } | undefined>>();\n\t\treturn collection => {\n\t\t\tconst key = collection.toLowerCase();\n\t\t\tlet viewer = viewers.get(key);\n\t\t\tif (viewer == null) {\n\t\t\t\tviewer = this.getFilterUserId(session, collection).then(\n\t\t\t\t\tid => (id != null ? { id: id } : undefined),\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t\tviewers.set(key, viewer);\n\t\t\t}\n\t\t\treturn viewer;\n\t\t};\n\t}\n\n\t/** Every collection the account can see, or a refusal when discovery itself failed. */\n\tprivate async getSearchCollections(session: ProviderAuthenticationSession): Promise<AzureOrganizationDescriptor[]> {\n\t\tconst collections = await this.getProviderResourcesForUser(session);\n\t\tif (collections == null) throw this.searchUnavailable('the account’s collections could not be read');\n\n\t\treturn collections;\n\t}\n\n\t/**\n\t * A collection's projects, refused when discovery was incomplete: a search bounded by an incomplete project set\n\t * would silently omit the missing projects' matches. `refresh` re-reads them rather than trusting the discovery\n\t * cache, which lives for the whole connection and so doesn't know a project created since.\n\t */\n\tprivate async getSearchProjects(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcollection: AzureOrganizationDescriptor,\n\t\trefresh: boolean = false,\n\t): Promise<AzureProjectDescriptor[]> {\n\t\tconst projects = refresh\n\t\t\t? await this.refreshSearchProjects(session, collection)\n\t\t\t: await this.getProviderProjectsForResources(session, [collection]);\n\t\tif (projects.metadata != null && projects.metadata.completeness !== 'complete') {\n\t\t\tthrow this.searchUnavailable(`the projects of the '${collection.name}' collection could not all be read`);\n\t\t}\n\n\t\treturn projects.values;\n\t}\n\n\tprivate readonly _projectRefreshes = new Map<\n\t\tstring,\n\t\tPromise<ProviderApiCollectionResult<AzureProjectDescriptor>>\n\t>();\n\n\t/**\n\t * Re-reads a collection's projects, sharing one read among the refreshes IN FLIGHT at once — a count batch or a\n\t * multi-repository drain refreshes the same collection once per scope, and each would otherwise re-read every\n\t * project page. Nothing outlives the read, so the next search still sees a project created after it.\n\t */\n\tprivate refreshSearchProjects(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcollection: AzureOrganizationDescriptor,\n\t): Promise<ProviderApiCollectionResult<AzureProjectDescriptor>> {\n\t\tconst key = `${this.discoveryKey(session)}:${collection.id}`;\n\t\tlet pending = this._projectRefreshes.get(key);\n\t\tif (pending == null) {\n\t\t\tpending = this.getProviderProjectsForResources(session, [collection], true).finally(() => {\n\t\t\t\tthis._projectRefreshes.delete(key);\n\t\t\t});\n\t\t\tthis._projectRefreshes.set(key, pending);\n\t\t}\n\n\t\treturn pending;\n\t}\n\n\t/**\n\t * Refuses dates the search can't apply before anything is read. The facade only checks that dates are\n\t * supported, not their shape, and a refusal here stays with its own scope in a count batch.\n\t */\n\tprivate assertSearchDates(criteria: { updatedAfter?: string; createdAfter?: string } | undefined): void {\n\t\tconst invalid = getInvalidAzureSearchDate(criteria);\n\t\tif (invalid != null) throw this.searchUnavailable(invalid);\n\t}\n\n\tprivate searchUnavailable(reason: string): IntegrationReadUnavailableError {\n\t\treturn new IntegrationReadUnavailableError(this.name, `search refused: ${reason}.`);\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tsuper.providerOnDisconnect();\n\t\tthis._pullRequestSearches.clear();\n\t\tthis._latestPullRequestSearches.clear();\n\t\tthis._collectionUserIds.clear();\n\t}\n\n\tprotected override async _requestForCurrentUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<Account | undefined> {\n\t\tconst azure = await this.authenticationService.apis.azure;\n\t\tconst user = azure\n\t\t\t? await azure.getCurrentUserOnServer(this, toTokenWithInfo(this.id, session), this.apiBaseUrlFor(session))\n\t\t\t: undefined;\n\t\treturn user\n\t\t\t? {\n\t\t\t\t\tprovider: this,\n\t\t\t\t\tid: user.id,\n\t\t\t\t\tname: user.name ?? undefined,\n\t\t\t\t\temail: user.email ?? undefined,\n\t\t\t\t\tavatarUrl: user.avatarUrl ?? undefined,\n\t\t\t\t\tusername: user.username ?? undefined,\n\t\t\t\t}\n\t\t\t: undefined;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"azureDevOpsServer.js","names":[],"sources":["../../../../../plus/integrations/src/providers/azureDevOpsServer.ts"],"sourcesContent":["import type { CollectionScopeFailure } from '@gitkraken/provider-apis';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { IssueSearchCriteria } from '@gitlens/git/models/issue.js';\nimport type { PullRequest, PullRequestSearchCriteria, PullRequestSorting } from '@gitlens/git/models/pullRequest.js';\nimport { defaultPullRequestSort } from '@gitlens/git/models/pullRequest.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { uuid } from '@gitlens/utils/crypto.js';\nimport type { Emitter } from '@gitlens/utils/event.js';\nimport { mapBounded } from '@gitlens/utils/promise.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type { ProviderAuthenticationSession, TokenWithInfo } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { GitSelfManagedHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport { IntegrationReadUnavailableError, RequestNotFoundError } from '../errors.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport type {\n\tIntegrationKey,\n\tProviderIssueSearchPage,\n\tProviderPullRequestCount,\n\tProviderPullRequestSearchPage,\n\tProviderSearchCount,\n} from '../models/integration.js';\nimport type { AzureOrganizationDescriptor, AzureProjectDescriptor } from './azure/models.js';\nimport type { AzurePullRequestSearchPosition } from './azure/search.js';\nimport {\n\tcompareAzurePullRequestSearchPositions,\n\tgetInvalidAzureSearchDate,\n\tisAzureWorkItemSearchFirstPage,\n\tparseAzurePullRequestSearchCursor,\n\ttoAzurePullRequestSearchCursorKey,\n\ttoAzurePullRequestSearchFilter,\n\ttoAzurePullRequestSearchPosition,\n\ttoAzureSearchPageSize,\n} from './azure/search.js';\nimport type { AzureReviewerRead } from './azureDevOps.js';\nimport {\n\tAzureDevOpsIntegrationBase,\n\tgetAzureRepositoryApiBaseUrl,\n\tgetAzureRepositoryIdentity,\n\tmarkMyGroupReviews,\n\tsameAzureName,\n\ttoAzureReviewerReads,\n\tuniqueAzureNames,\n} from './azureDevOps.js';\nimport type {\n\tProviderApiCollectionResult,\n\tProviderAzureResource,\n\tProviderPullRequest,\n\tProviderRepoInput,\n\tProviderReposInput,\n} from './models.js';\nimport {\n\tfromProviderPullRequest,\n\tgetProviderPullRequestIdentity,\n\tisNativePullRequest,\n\tprovidersMetadata,\n\tPullRequestFilter,\n\ttoProviderPullRequestStates,\n} from './models.js';\nimport type { ProvidersApi } from './providersApi.js';\n\n/**\n * Whether `value` can stand as one route segment. Encoding keeps `.` and `..` as they are, and a URL resolves them\n * as directory steps, so a name of either would address a different route than the one named.\n */\nfunction isAzureRouteSegment(value: string): boolean {\n\treturn value.length > 0 && value !== '.' && value !== '..';\n}\n\n/**\n * Runs one scope of a count probe, answering a refusal of THAT scope (see `searchUnavailable`) as the error itself,\n * which the facade reports and drops without failing the scopes around it. Anything else — a rejected credential,\n * a failed request — still rejects, so the batch reports it.\n */\nasync function refusalAsCount<T>(count: () => Promise<T>): Promise<T | IntegrationReadUnavailableError> {\n\ttry {\n\t\treturn await count();\n\t} catch (ex) {\n\t\tif (ex instanceof IntegrationReadUnavailableError) return ex;\n\n\t\tthrow ex;\n\t}\n}\n\nfunction findAzureCollection(\n\tcollections: readonly AzureOrganizationDescriptor[],\n\tname: string,\n): AzureOrganizationDescriptor | undefined {\n\treturn collections.find(c => sameAzureName(c.name, name) || c.id === name);\n}\n\n/**\n * The most pull requests one facet of a filtered pull-request search drains. Azure has no server-side text, date or\n * draft filter and no ordering, so a facet is read in full before any of them apply; past this bound the facet's\n * unread rows could sort anywhere, and the search reports itself truncated instead of guessing.\n */\nconst azurePullRequestSearchFacetLimit = 1000;\n\n/**\n * The page size a facet drain reads. The SDK's own default for both pull request reads, and the size it sizes\n * `$skip` by, so the budget above counts what was actually read.\n */\nconst azurePullRequestSearchPageSize = 100;\n\n/**\n * How many scopes of one work-item count batch query at once. The facade already runs several batches concurrently,\n * so this multiplies with it: kept small, so a large count preview can't flood a self-hosted server.\n */\nconst azureIssueCountConcurrency = 2;\n\n/**\n * How long a drained pull-request search keeps serving its continuation pages after the last one was read, and the\n * most it lives however actively it is paged. A pagination that keeps reading stays on one consistent drain; one\n * left idle, or paged for longer than that, is read again.\n */\nconst pullRequestSearchIdleTtl = 5 * 60 * 1000;\nconst pullRequestSearchMaxTtl = 30 * 60 * 1000;\n\n/**\n * How long a count reuses the drain a search of its query just read. Long enough for a count shown next to the\n * search it previews to agree with it, short enough that a count polled next to an unapplied filter stays live: the\n * window runs from the drain, not from the last count, so polling never extends it.\n */\nconst pullRequestCountReuseTtl = 60 * 1000;\n\n/**\n * Kept unconverted, so each page converts its rows with the projection its own caller asked for: the drain is shared\n * by every page of a query, whatever `summary` each page passes.\n */\ninterface AzurePullRequestSearchRow {\n\tpr: ProviderPullRequest;\n\tproject: AzureProjectDescriptor;\n\tcurrentAccount: { id: string } | undefined;\n\tposition: AzurePullRequestSearchPosition;\n}\n\ninterface AzurePullRequestSearchDrain {\n\trows: AzurePullRequestSearchRow[];\n\ttruncated: boolean;\n\t/** Collections whose groups couldn't be read, so what only those groups review is missing. */\n\tfailures?: CollectionScopeFailure[];\n}\n\n/**\n * Everything that decides a pull-request search's result set and order, as the value its cursor is fingerprinted\n * from. `pageSize` is left out on purpose: it only decides where a page ends, which the keyset cursor already\n * carries.\n */\nfunction toPullRequestSearchQuery(\n\toptions: { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria },\n\tsort: PullRequestSorting,\n): unknown {\n\tconst criteria = options.criteria;\n\treturn {\n\t\trepos: options.repos?.map(r => [r.namespace, r.project ?? null, r.name]),\n\t\torg: options.org ?? null,\n\t\trelationships: [...new Set(criteria?.relationships ?? [])].sort(),\n\t\tstates: [...new Set(criteria?.states ?? [])].sort(),\n\t\ttext: criteria?.text?.trim() ?? null,\n\t\tdraft: criteria?.draft ?? null,\n\t\tupdatedAfter: criteria?.updatedAfter ?? null,\n\t\tcreatedAfter: criteria?.createdAfter ?? null,\n\t\tsort: sort,\n\t};\n}\n\nconst serverMetadata = providersMetadata[GitSelfManagedHostIntegrationId.AzureDevOpsServer];\nconst serverAuthProvider = Object.freeze({ id: serverMetadata.id, scopes: serverMetadata.scopes });\n\nexport class AzureDevOpsServerIntegration extends AzureDevOpsIntegrationBase<GitSelfManagedHostIntegrationId.AzureDevOpsServer> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = serverAuthProvider;\n\treadonly id = GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\tprotected readonly key: IntegrationKey<GitSelfManagedHostIntegrationId.AzureDevOpsServer>;\n\treadonly name: string = 'Azure DevOps Server';\n\n\tconstructor(\n\t\tctx: IntegrationServiceContext,\n\t\tauthenticationService: IntegrationAuthenticationService,\n\t\tgetProvidersApi: () => Promise<ProvidersApi>,\n\t\tdidChangeConnection: Emitter<IntegrationConnectionChangeEvent>,\n\t\treadonly domain: string,\n\t) {\n\t\tsuper(ctx, authenticationService, getProvidersApi, didChangeConnection);\n\t\tthis.key = `${this.id}:${this.domain}`;\n\t}\n\n\tprotected override apiBaseUrlFor(session: ProviderAuthenticationSession): string {\n\t\treturn this.getSelfManagedApiBaseUrl(session);\n\t}\n\n\t/**\n\t * The credential AND the installation it is used against: one integration instance serves every installation on\n\t * its host (context paths differ, the host doesn't), and the same token connected to two of them must not reuse\n\t * one installation's collections or projects on the other.\n\t */\n\tprotected override discoveryKey(session: ProviderAuthenticationSession): string {\n\t\treturn `${session.accessToken}:${this.apiBaseUrlFor(session)}`;\n\t}\n\n\tprotected override getRepositoriesApiBaseUrl(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos: ProviderReposInput,\n\t): string {\n\t\t// Requests append each repository's organization (its collection), which an address naming that collection\n\t\t// already ends with. Only repositories of a single organization can share one base; mixed ones keep the\n\t\t// address, so another collection's repository stays below it (see `resolveRepository`).\n\t\tconst owners = new Set(repos.map(r => (typeof r === 'object' ? r.namespace : undefined)));\n\t\tconst [owner] = owners;\n\t\tconst baseUrl = this.apiBaseUrlFor(session);\n\t\treturn owners.size === 1 && owner != null\n\t\t\t? getAzureRepositoryApiBaseUrl(baseUrl, { owner: owner, virtualDirectory: undefined })\n\t\t\t: baseUrl;\n\t}\n\n\tprotected override getApiOptions(\n\t\tsession: ProviderAuthenticationSession,\n\t\tdoNotConvertToPat: boolean = false,\n\t): {\n\t\ttokenWithInfo: TokenWithInfo<GitSelfManagedHostIntegrationId.AzureDevOpsServer>;\n\t\toptions: { isPAT: boolean; baseUrl?: string };\n\t} {\n\t\tconst { options, ...rest } = super.getApiOptions(session, doNotConvertToPat);\n\t\treturn {\n\t\t\t...rest,\n\t\t\toptions: { ...options, baseUrl: this.apiBaseUrlFor(session) },\n\t\t};\n\t}\n\n\tprotected override getCollectionApiOptions(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcollection: string,\n\t): { isPAT: boolean; baseUrl?: string } {\n\t\treturn {\n\t\t\t...super.getCollectionApiOptions(session, collection),\n\t\t\tbaseUrl: this.collectionApiBaseUrl(session, collection),\n\t\t};\n\t}\n\n\t/** The base a request below `collection` appends to: the installation, whichever the address already ends in. */\n\tprotected override collectionApiBaseUrl(session: ProviderAuthenticationSession, collection: string): string {\n\t\treturn getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), {\n\t\t\towner: collection,\n\t\t\tvirtualDirectory: undefined,\n\t\t});\n\t}\n\n\t/**\n\t * Only the server level lists the collections: below one, Azure DevOps Server answers `projectCollections` with a\n\t * 404 page. On that 404 an address that names a collection reads the collection's own connection data, whose\n\t * `webApplicationRelativeDirectory` spells it, and reports it as the one collection visible there.\n\t */\n\tprotected override async requestResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tuserId: string,\n\t): Promise<ProviderAzureResource[] | undefined> {\n\t\ttry {\n\t\t\treturn await super.requestResourcesForUser(session, userId);\n\t\t} catch (ex) {\n\t\t\tif (!(ex instanceof RequestNotFoundError)) throw ex;\n\n\t\t\tconst azure = await this.authenticationService.apis.azure;\n\t\t\tconst addressed = await azure?.getAddressedCollection(\n\t\t\t\tthis,\n\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\tthis.apiBaseUrlFor(session),\n\t\t\t);\n\t\t\tif (addressed == null) throw ex;\n\n\t\t\treturn [addressed];\n\t\t}\n\t}\n\n\t/**\n\t * The filtered work-item search: one WIQL query per page over ONE collection, then one detail read for the\n\t * page's slice. See {@link resolveWorkItemSearchScope} for why a search never spans several collections.\n\t */\n\tprotected override async searchProviderIssuesPage(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: {\n\t\t\trepos?: ProviderRepoInput[];\n\t\t\torg?: string;\n\t\t\tcriteria?: IssueSearchCriteria;\n\t\t\tcursor?: string;\n\t\t\tpageSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<ProviderIssueSearchPage | undefined> {\n\t\tconst azure = await this.authenticationService.apis.azure;\n\t\tif (azure == null) return undefined;\n\n\t\tthis.assertSearchDates(options.criteria);\n\t\t// A first page re-reads the collection's projects, so a project created since discovery is known before its\n\t\t// work items can turn up in a collection-wide query; a continuation keeps the set its first page resolved.\n\t\t// The facade's page-1 marker (`broadenIssues` retries a failed first page with it) is a first page too.\n\t\tconst scope = await this.resolveWorkItemSearchScope(\n\t\t\tsession,\n\t\t\toptions.repos,\n\t\t\toptions.org,\n\t\t\tisAzureWorkItemSearchFirstPage(options.cursor),\n\t\t);\n\t\treturn azure.searchWorkItemsPage(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tscope.collection,\n\t\t\t{\n\t\t\t\tbaseUrl: this.collectionApiBaseUrl(session, scope.collection),\n\t\t\t\tcriteria: options.criteria,\n\t\t\t\tprojectNames: scope.projectNames,\n\t\t\t\tresolveProject: name => scope.projects.find(p => sameAzureName(p.name, name)),\n\t\t\t\tcursor: options.cursor,\n\t\t\t\tpageSize: options.pageSize,\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\t/**\n\t * Counts each scope with the WIQL its search would page through, so a count never previews constraints the read\n\t * doesn't apply. A scope this provider refuses (see {@link resolveWorkItemSearchScope}) is answered as that\n\t * refusal, so the facade drops only it; a request failure still fails the batch, which the facade isolates from\n\t * the other batches.\n\t */\n\tprotected override async countProviderIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tscopes: readonly { repos?: ProviderRepoInput[]; org?: string; criteria?: IssueSearchCriteria }[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<ProviderSearchCount[] | undefined> {\n\t\tconst azure = await this.authenticationService.apis.azure;\n\t\tif (azure == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\treturn mapBounded(scopes, azureIssueCountConcurrency, s =>\n\t\t\trefusalAsCount(async () => {\n\t\t\t\tthis.assertSearchDates(s.criteria);\n\t\t\t\t// Re-reads the projects like a first search page does, so a count and the page it previews resolve\n\t\t\t\t// the same project set — a newer project can't be counted by one and refused or missed by the other.\n\t\t\t\tconst scope = await this.resolveWorkItemSearchScope(session, s.repos, s.org, true);\n\t\t\t\treturn azure.countWorkItems(\n\t\t\t\t\tthis,\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\tscope.collection,\n\t\t\t\t\t{\n\t\t\t\t\t\tbaseUrl: this.collectionApiBaseUrl(session, scope.collection),\n\t\t\t\t\t\tcriteria: s.criteria,\n\t\t\t\t\t\tprojectNames: scope.projectNames,\n\t\t\t\t\t},\n\t\t\t\t\tcancellation,\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * The ONE collection a work-item search runs in, and the projects that bound it.\n\t *\n\t * A WIQL query is collection-scoped, and ordering a union of several would need every match's details rather\n\t * than one page's, so a search never spans collections. `org` names the collection; without it the repositories'\n\t * collection is used, and without either the only collection the account can see. Anything else — repositories\n\t * in two collections, or several visible collections and nothing to choose one — is refused rather than\n\t * narrowed to whichever came first.\n\t *\n\t * Repositories bound the query by their PROJECT, since Azure work items belong to projects rather than to\n\t * repositories, and every project must be one the collection actually has: WIQL answers an unknown project name\n\t * with an empty result, which would read as \"nothing matched\".\n\t */\n\tprivate async resolveWorkItemSearchScope(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos: ProviderRepoInput[] | undefined,\n\t\torg: string | undefined,\n\t\trefreshProjects: boolean = false,\n\t): Promise<{ collection: string; projects: AzureProjectDescriptor[]; projectNames?: string[] }> {\n\t\tconst repoCollections = uniqueAzureNames(repos?.map(r => r.namespace) ?? []);\n\t\tif (repoCollections.length > 1) {\n\t\t\tthrow this.searchUnavailable(\n\t\t\t\t'the repositories span several collections; search each collection separately',\n\t\t\t);\n\t\t}\n\t\tif (org && repoCollections.length === 1 && !sameAzureName(org, repoCollections[0])) {\n\t\t\tthrow this.searchUnavailable(`the repositories are not in the '${org}' collection`);\n\t\t}\n\n\t\tconst collections = await this.getSearchCollections(session);\n\t\tconst name = org || repoCollections[0] || (collections.length === 1 ? collections[0].name : undefined);\n\t\tif (name == null) {\n\t\t\tthrow this.searchUnavailable(\n\t\t\t\t`the account can see ${collections.length} collections; pass \\`org\\` to choose the one to search`,\n\t\t\t);\n\t\t}\n\n\t\tconst collection = findAzureCollection(collections, name);\n\t\tif (collection == null) throw this.searchUnavailable(`the '${name}' collection is not visible`);\n\n\t\tconst projects = await this.getSearchProjects(session, collection, refreshProjects);\n\t\tif (!repos?.length) return { collection: collection.name, projects: projects };\n\n\t\tconst projectNames = uniqueAzureNames(\n\t\t\trepos.map(r => {\n\t\t\t\tconst projectName = getAzureRepositoryIdentity({\n\t\t\t\t\towner: r.namespace,\n\t\t\t\t\tname: r.name,\n\t\t\t\t\tproject: r.project,\n\t\t\t\t}).projectName;\n\t\t\t\tif (projectName == null) {\n\t\t\t\t\tthrow this.searchUnavailable(`repository '${r.namespace}/${r.name}' names no project`);\n\t\t\t\t}\n\n\t\t\t\tconst project = projects.find(p => sameAzureName(p.name, projectName));\n\t\t\t\tif (project == null) {\n\t\t\t\t\tthrow this.searchUnavailable(`project '${projectName}' is not visible in '${collection.name}'`);\n\t\t\t\t}\n\n\t\t\t\treturn project.name;\n\t\t\t}),\n\t\t);\n\t\treturn { collection: collection.name, projects: projects, projectNames: projectNames };\n\t}\n\n\t/**\n\t * The filtered pull-request search. Azure's pull request query can only narrow by creator, reviewer, status and\n\t * repository, so every facet is drained (see {@link drainPullRequestSearch}) and the criteria it can't express\n\t * are applied to the complete facets; the union is then ordered and served from a keyset cursor.\n\t */\n\tprotected override async searchProviderPullRequestsPage(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: {\n\t\t\trepos?: ProviderRepoInput[];\n\t\t\torg?: string;\n\t\t\tcriteria?: PullRequestSearchCriteria;\n\t\t\tcursor?: string;\n\t\t\tpageSize?: number;\n\t\t\tsummary?: boolean;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<ProviderPullRequestSearchPage | undefined> {\n\t\tthis.assertSearchDates(options.criteria);\n\t\tconst sort = options.criteria?.sort ?? defaultPullRequestSort;\n\t\tconst query = toPullRequestSearchQuery(options, sort);\n\t\tconst key = toAzurePullRequestSearchCursorKey(query);\n\t\tconst cursor = parseAzurePullRequestSearchCursor(options.cursor, key);\n\t\t// A first page drains fresh, into a drain of its own, and its continuations page through that one: the cache\n\t\t// keeps it alive for as long as the pagination keeps reading it (an access TTL), so rows can't move between\n\t\t// pages, and a later first page of the same query never replaces it. A continuation whose drain is gone is\n\t\t// refused, like an expired work-item snapshot: resuming against a re-drain could skip or repeat a pull request\n\t\t// whose sort date changed. The drain is keyed by credential and installation too, so a cursor handed to\n\t\t// another connection finds nothing and is refused the same way.\n\t\tconst drainId = cursor?.drain ?? uuid();\n\t\tconst drainKey = this.pullRequestSearchCacheKey(session, query, drainId);\n\t\t// A continuation only ever READS its drain: creating one under its id would be the re-drain refused above.\n\t\tconst drainFound =\n\t\t\tcursor != null\n\t\t\t\t? this._pullRequestSearches.get(drainKey)\n\t\t\t\t: this.getPullRequestSearch(this._pullRequestSearches, drainKey, session, options, sort, cancellation);\n\t\tif (drainFound == null) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pull request search results expired or belong to another connection; restart the read without a cursor',\n\t\t\t);\n\t\t}\n\n\t\tconst drain = await drainFound;\n\t\tif (cursor == null) {\n\t\t\t// Also the query's latest drain, which a count of the same query reads rather than draining again. Stored\n\t\t\t// through `getOrCreate`, which applies the cache's capacity and expiry; `set` applies neither.\n\t\t\tconst latestKey = this.pullRequestSearchCacheKey(session, query);\n\t\t\tthis._latestPullRequestSearches.delete(latestKey);\n\t\t\tvoid this._latestPullRequestSearches.getOrCreate(latestKey, () => Promise.resolve(drain));\n\t\t}\n\n\t\tconst pageSize = toAzureSearchPageSize(options.pageSize, 50, 100);\n\t\tconst start =\n\t\t\tcursor == null\n\t\t\t\t? 0\n\t\t\t\t: drain.rows.findIndex(r => compareAzurePullRequestSearchPositions(r.position, cursor.after, sort) > 0);\n\t\tconst slice = start < 0 ? [] : drain.rows.slice(start, start + pageSize);\n\t\tconst page = cursor?.page ?? 1;\n\t\tconst last = slice.at(-1);\n\t\tconst hasMore = last != null && start + slice.length < drain.rows.length;\n\t\treturn {\n\t\t\tvalues: slice.map(r =>\n\t\t\t\tfromProviderPullRequest(r.pr, this, {\n\t\t\t\t\tproject: r.project,\n\t\t\t\t\tcurrentAccount: r.currentAccount,\n\t\t\t\t\tprojection: options.summary ? 'search-summary' : 'search',\n\t\t\t\t}),\n\t\t\t),\n\t\t\tcursor: hasMore\n\t\t\t\t? JSON.stringify({ key: key, drain: drainId, page: page + 1, after: last.position })\n\t\t\t\t: undefined,\n\t\t\thasMore: hasMore,\n\t\t\tpage: page,\n\t\t\ttruncated: drain.truncated,\n\t\t\ttotalCount: drain.truncated ? undefined : drain.rows.length,\n\t\t\tmetadata: drain.failures != null ? { completeness: 'partial', failures: drain.failures } : undefined,\n\t\t};\n\t}\n\n\t/**\n\t * Counts each scope from the latest drain of its query, the one a search's first page just read, or drains it; see\n\t * {@link searchProviderPullRequestsPage}.\n\t */\n\tprotected override async countProviderPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\tscopes: readonly { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria }[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<(ProviderPullRequestCount | Error)[] | undefined> {\n\t\t// One scope at a time: each is a drain that already fans out over its facets, and the facade runs several\n\t\t// batches of scopes at once, so a concurrent batch would multiply the requests reaching the server.\n\t\treturn mapBounded(scopes, 1, s =>\n\t\t\trefusalAsCount(async () => {\n\t\t\t\tthis.assertSearchDates(s.criteria);\n\t\t\t\tconst sort = s.criteria?.sort ?? defaultPullRequestSort;\n\t\t\t\tconst drain = await this.getPullRequestSearch(\n\t\t\t\t\tthis._latestPullRequestSearches,\n\t\t\t\t\tthis.pullRequestSearchCacheKey(session, toPullRequestSearchQuery(s, sort)),\n\t\t\t\t\tsession,\n\t\t\t\t\ts,\n\t\t\t\t\tsort,\n\t\t\t\t\tcancellation,\n\t\t\t\t);\n\t\t\t\t// Past the drain budget the true total is unknown, so it is reported as uncounted rather than understated.\n\t\t\t\treturn { count: drain.truncated ? undefined : drain.rows.length };\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * The drain each pagination pages through, one per first page (see {@link searchProviderPullRequestsPage}), so\n\t * the 50 most recently read paginations are kept for as long as they keep reading.\n\t */\n\tprivate readonly _pullRequestSearches = new PromiseCache<string, AzurePullRequestSearchDrain>({\n\t\tcapacity: 50,\n\t\taccessTTL: pullRequestSearchIdleTtl,\n\t\tcreateTTL: pullRequestSearchMaxTtl,\n\t});\n\t/**\n\t * Each query's latest drain, which its count reads for {@link pullRequestCountReuseTtl}. Apart from the\n\t * paginations' drains, so counting and starting searches never evicts a pagination still being read.\n\t */\n\tprivate readonly _latestPullRequestSearches = new PromiseCache<string, AzurePullRequestSearchDrain>({\n\t\tcapacity: 20,\n\t\tcreateTTL: pullRequestCountReuseTtl,\n\t});\n\n\t/**\n\t * Keyed by the query itself, not its cursor fingerprint, so two queries whose fingerprints collide can never share\n\t * a drain; by credential and installation, so two accounts or installations never read each other's results; and\n\t * by `drainId` for a pagination's own drain, left out for the query's latest one.\n\t */\n\tprivate pullRequestSearchCacheKey(\n\t\tsession: ProviderAuthenticationSession,\n\t\tquery: unknown,\n\t\tdrainId?: string,\n\t): string {\n\t\treturn JSON.stringify([session.accessToken, this.apiBaseUrlFor(session), query, drainId ?? null]);\n\t}\n\n\tprivate getPullRequestSearch(\n\t\tcache: PromiseCache<string, AzurePullRequestSearchDrain>,\n\t\tcacheKey: string,\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria },\n\t\tsort: PullRequestSorting,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<AzurePullRequestSearchDrain> {\n\t\treturn cache.getOrCreate(\n\t\t\tcacheKey,\n\t\t\t(_cacheable, signal) => this.drainPullRequestSearch(session, options, sort, signal),\n\t\t\t{ cancellation: cancellation },\n\t\t);\n\t}\n\n\t/**\n\t * Every pull request matching the search, filtered, deduplicated and ordered.\n\t *\n\t * One facet per (repository or project) × relationship: `Author` reads by creator, while `Assignee` and\n\t * `ReviewRequested` both read by reviewer, since Azure has no assignee distinct from its reviewers: every open pull\n\t * request, kept when it names the user or one of the user's groups, and closed or merged ones through Azure's filter\n\t * for the user (see `toAzureReviewerReads`). Each facet is drained to {@link azurePullRequestSearchFacetLimit}; a\n\t * facet that still has more marks the whole result truncated, because its unread rows could sort anywhere in the\n\t * union. A facet that fails fails the search: a union missing one facet would be served in an order it doesn't\n\t * have. A collection whose groups can't be read is the exception: its facets still read what names the user, and\n\t * the result is reported incomplete with that collection's failure.\n\t */\n\tprivate async drainPullRequestSearch(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria },\n\t\tsort: PullRequestSorting,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<AzurePullRequestSearchDrain> {\n\t\tconst criteria = options.criteria;\n\t\t// Built before any request, so criteria it can't apply fail the search before the facets are drained.\n\t\tconst matches = toAzurePullRequestSearchFilter(criteria);\n\t\tconst relationships = new Set(criteria?.relationships ?? []);\n\t\tconst wantAuthored = relationships.has(PullRequestFilter.Author);\n\t\tconst wantReviewed =\n\t\t\trelationships.has(PullRequestFilter.Assignee) || relationships.has(PullRequestFilter.ReviewRequested);\n\n\t\t// A drain runs for a first page (or once its predecessor expired), so it re-reads the projects it fans out\n\t\t// over: an org search trusting the connection-long discovery cache would silently skip a newer project.\n\t\tconst facets = await this.resolvePullRequestSearchFacets(session, options.repos, options.org);\n\n\t\t// The current user's id is resolved PER COLLECTION: Azure DevOps Server gives one person a different\n\t\t// identity id in each collection than at the server level, and a creator/reviewer filter only matches the\n\t\t// collection's. The server-level id matches nothing there and would serve an empty result as if nobody had\n\t\t// authored anything.\n\t\tconst userIds = new Map<string, string>();\n\t\tif (relationships.size > 0) {\n\t\t\tfor (const collection of uniqueAzureNames(facets.map(f => f.collection))) {\n\t\t\t\tconst userId = await this.getFilterUserId(session, collection);\n\t\t\t\tif (userId == null) {\n\t\t\t\t\tthrow this.searchUnavailable(`the current user could not be resolved in '${collection}'`);\n\t\t\t\t}\n\n\t\t\t\tuserIds.set(collection, userId);\n\t\t\t}\n\t\t}\n\n\t\t// Every group the user is in, per collection, which the reviewer facets keep pull requests for too (see\n\t\t// `toAzureReviewerReads`). A collection whose groups can't be read (e.g. a token without the Identity scope)\n\t\t// keeps what names the user and is reported as a failure of that collection, so the search is incomplete\n\t\t// rather than refused, and a credential its other reads accept isn't treated as a dead one.\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tconst groupsByCollection = new Map(\n\t\t\twantReviewed\n\t\t\t\t? await Promise.all(\n\t\t\t\t\t\tArray.from(userIds, async ([collection, userId]) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\t\t\tawait this.getReviewerGroupIds(session, collection, userId),\n\t\t\t\t\t\t\t\t] as const;\n\t\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\t\tconst resourceId = facets.find(f => f.collection === collection)!.project.resourceId;\n\t\t\t\t\t\t\t\tfailures.push(\n\t\t\t\t\t\t\t\t\ttoCollectionScopeFailure({ providerId: this.id, resourceId: resourceId }, ex),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn [collection, new Set<string>()] as const;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t: [],\n\t\t);\n\t\tconst groupIds = new Set([...groupsByCollection.values()].flatMap(ids => [...ids]));\n\n\t\tconst states = toProviderPullRequestStates(criteria?.states?.length ? criteria.states : 'open');\n\t\tconst filtersFor = (\n\t\t\tcollection: string,\n\t\t): { filter: AzureReviewerRead['filter'] & { authorLogin?: string }; keep?: AzureReviewerRead['keep'] }[] => {\n\t\t\tif (relationships.size === 0) return [{ filter: {} }];\n\n\t\t\tconst userId = userIds.get(collection)!;\n\t\t\treturn [\n\t\t\t\t...(wantAuthored ? [{ filter: { authorLogin: userId } }] : []),\n\t\t\t\t...(wantReviewed ? toAzureReviewerReads(userId, groupsByCollection.get(collection)!, states) : []),\n\t\t\t];\n\t\t};\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);\n\t\tconst drains = await mapBounded(\n\t\t\tfacets.flatMap(facet => filtersFor(facet.collection).map(f => ({ facet: facet, ...f }))),\n\t\t\tproviderFanOutConcurrency,\n\t\t\tasync ({ facet, filter, keep }) => {\n\t\t\t\tconst baseUrl = this.collectionApiBaseUrl(session, facet.collection);\n\t\t\t\tconst values: ProviderPullRequest[] = [];\n\t\t\t\tlet page = 1;\n\t\t\t\tfor (let read = 1; ; read++) {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\tconst input = {\n\t\t\t\t\t\t...apiOptions,\n\t\t\t\t\t\t// A reviewer facet narrows the states itself (see `toAzureReviewerReads`).\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t\t...filter,\n\t\t\t\t\t\tpage: page,\n\t\t\t\t\t\tpageSize: azurePullRequestSearchPageSize,\n\t\t\t\t\t\tbaseUrl: baseUrl,\n\t\t\t\t\t};\n\t\t\t\t\tconst result =\n\t\t\t\t\t\tfacet.repository != null\n\t\t\t\t\t\t\t? await api\n\t\t\t\t\t\t\t\t\t.getPullRequestsForRepo(\n\t\t\t\t\t\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tnamespace: facet.collection,\n\t\t\t\t\t\t\t\t\t\t\tname: facet.repository,\n\t\t\t\t\t\t\t\t\t\t\tproject: facet.project.name,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tinput,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t.then(r => ({\n\t\t\t\t\t\t\t\t\t\tvalues: r.values,\n\t\t\t\t\t\t\t\t\t\tmore: r.paging?.more === true,\n\t\t\t\t\t\t\t\t\t\tnextPage: r.paging?.nextPage,\n\t\t\t\t\t\t\t\t\t}))\n\t\t\t\t\t\t\t: await api\n\t\t\t\t\t\t\t\t\t.getPullRequestsForAzureProject(\n\t\t\t\t\t\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t\t\t\t\t\t{ namespace: facet.collection, project: facet.project.name },\n\t\t\t\t\t\t\t\t\t\tinput,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t.then(r =>\n\t\t\t\t\t\t\t\t\t\tr != null\n\t\t\t\t\t\t\t\t\t\t\t? { values: r.data, more: r.hasMore, nextPage: r.nextPage ?? undefined }\n\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\tif (result == null) throw new Error('Azure DevOps returned no pull request page');\n\n\t\t\t\t\tvalues.push(...result.values);\n\t\t\t\t\tif (!result.more) return { facet: facet, keep: keep, values: values, truncated: false };\n\t\t\t\t\t// Follows the page the provider says comes next rather than assuming one: a continuation that\n\t\t\t\t\t// doesn't advance would re-read or skip rows, so it fails the facet — and with it the search —\n\t\t\t\t\t// instead of serving a union built from it.\n\t\t\t\t\tif (result.nextPage == null || result.nextPage <= page) {\n\t\t\t\t\t\tthrow new Error('Azure DevOps returned no advancing pull request continuation');\n\t\t\t\t\t}\n\t\t\t\t\t// The budget counts pages READ, not rows kept: several states are one `status=all` query the SDK\n\t\t\t\t\t// narrows client-side, so counting survivors would keep paging through rows it discards.\n\t\t\t\t\tif (read * azurePullRequestSearchPageSize >= azurePullRequestSearchFacetLimit) {\n\t\t\t\t\t\treturn { facet: facet, keep: keep, values: values, truncated: true };\n\t\t\t\t\t}\n\n\t\t\t\t\tpage = result.nextPage;\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tconst rows = new Map<string, AzurePullRequestSearchRow>();\n\t\tfor (const { facet, keep, values } of drains) {\n\t\t\tfor (const pr of values) {\n\t\t\t\tif (keep != null && !keep(pr)) continue;\n\t\t\t\tif (!matches(pr)) continue;\n\n\t\t\t\tconst identity =\n\t\t\t\t\tgetProviderPullRequestIdentity(pr) ?? `project:${facet.collection}/${facet.project.name}:${pr.id}`;\n\t\t\t\tif (rows.has(identity)) continue;\n\n\t\t\t\tconst userId = userIds.get(facet.collection);\n\t\t\t\trows.set(identity, {\n\t\t\t\t\tpr: markMyGroupReviews(pr, groupIds),\n\t\t\t\t\tproject: facet.project,\n\t\t\t\t\tcurrentAccount: userId != null ? { id: userId } : undefined,\n\t\t\t\t\tposition: toAzurePullRequestSearchPosition(pr, sort, identity),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\trows: [...rows.values()].sort((a, b) =>\n\t\t\t\tcompareAzurePullRequestSearchPositions(a.position, b.position, sort),\n\t\t\t),\n\t\t\ttruncated: failures.length > 0 || drains.some(d => d.truncated),\n\t\t\tfailures: failures.length > 0 ? failures : undefined,\n\t\t};\n\t}\n\n\t/**\n\t * The repositories or projects a pull-request search reads, each with its collection and project descriptor.\n\t *\n\t * Repositories are read one by one; an `org` is its collection's projects; a search bounded only by the current\n\t * user reads every project the account can see, the same breadth as the account-wide pull request read.\n\t */\n\tprivate async resolvePullRequestSearchFacets(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos: ProviderRepoInput[] | undefined,\n\t\torg: string | undefined,\n\t): Promise<{ collection: string; project: AzureProjectDescriptor; repository?: string }[]> {\n\t\tconst collections = await this.getSearchCollections(session);\n\t\tconst collectionFor = (name: string): AzureOrganizationDescriptor => {\n\t\t\tconst collection = findAzureCollection(collections, name);\n\t\t\tif (collection == null) throw this.searchUnavailable(`the '${name}' collection is not visible`);\n\n\t\t\treturn collection;\n\t\t};\n\n\t\tif (repos?.length) {\n\t\t\tif (org && repos.some(r => !sameAzureName(r.namespace, org))) {\n\t\t\t\tthrow this.searchUnavailable(`the repositories are not all in the '${org}' collection`);\n\t\t\t}\n\n\t\t\t// One discovery per distinct collection, however many of its repositories the search names.\n\t\t\tconst projectsByCollection = new Map<string, Promise<AzureProjectDescriptor[]>>();\n\t\t\tconst projectsOf = (collection: AzureOrganizationDescriptor): Promise<AzureProjectDescriptor[]> => {\n\t\t\t\tlet projects = projectsByCollection.get(collection.id);\n\t\t\t\tif (projects == null) {\n\t\t\t\t\tprojects = this.getSearchProjects(session, collection, true);\n\t\t\t\t\tprojectsByCollection.set(collection.id, projects);\n\t\t\t\t}\n\t\t\t\treturn projects;\n\t\t\t};\n\n\t\t\treturn Promise.all(\n\t\t\t\trepos.map(async r => {\n\t\t\t\t\tconst collection = collectionFor(r.namespace);\n\t\t\t\t\tconst identity = getAzureRepositoryIdentity({\n\t\t\t\t\t\towner: r.namespace,\n\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\tproject: r.project,\n\t\t\t\t\t});\n\t\t\t\t\tif (identity.projectName == null) {\n\t\t\t\t\t\tthrow this.searchUnavailable(`repository '${r.namespace}/${r.name}' names no project`);\n\t\t\t\t\t}\n\t\t\t\t\t// The repository becomes a URL segment the SDK encodes but doesn't validate, and a segment of `..`\n\t\t\t\t\t// survives encoding and resolves to the project-wide route: refused rather than widened.\n\t\t\t\t\tif (!isAzureRouteSegment(identity.repositoryName)) {\n\t\t\t\t\t\tthrow this.searchUnavailable(`repository '${r.namespace}/${r.name}' is not a valid name`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst project = (await projectsOf(collection)).find(p =>\n\t\t\t\t\t\tsameAzureName(p.name, identity.projectName!),\n\t\t\t\t\t);\n\t\t\t\t\tif (project == null) {\n\t\t\t\t\t\tthrow this.searchUnavailable(\n\t\t\t\t\t\t\t`project '${identity.projectName}' is not visible in '${collection.name}'`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { collection: collection.name, project: project, repository: identity.repositoryName };\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst scoped = org ? [collectionFor(org)] : collections;\n\t\tconst projects = await Promise.all(scoped.map(c => this.getSearchProjects(session, c, true)));\n\t\treturn projects.flat().map(p => ({ collection: p.resourceName, project: p }));\n\t}\n\n\tprivate readonly _collectionUserIds = new PromiseCache<string, string | undefined>({ capacity: 50 });\n\n\t/**\n\t * The current user's identity id in `collection`, read from the collection's own connection data, which is\n\t * what Azure's creator and reviewer filters compare against. Cached per credential and collection, since it\n\t * can't change for either.\n\t */\n\tprotected override getFilterUserId(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcollection: string,\n\t): Promise<string | undefined> {\n\t\tconst baseUrl = this.collectionApiBaseUrl(session, collection);\n\t\treturn this._collectionUserIds.getOrCreate(\n\t\t\tJSON.stringify([session.accessToken, baseUrl, collection.toLowerCase()]),\n\t\t\tasync cacheable => {\n\t\t\t\tconst azure = await this.authenticationService.apis.azure;\n\t\t\t\tconst user = await azure?.getCurrentUserOnServer(\n\t\t\t\t\tthis,\n\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\t`${baseUrl}/${encodeURIComponent(collection)}`,\n\t\t\t\t);\n\t\t\t\t// A response without an identity must not refuse later relationship searches from a cached miss.\n\t\t\t\tif (user?.id == null) {\n\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t}\n\n\t\t\t\treturn user?.id;\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Resolved per collection, which a raw row names as its repository owner: a collection's pull requests carry the\n\t * id {@link getFilterUserId} resolves there, never the server-level account's. A row that names no collection or\n\t * has no author to match gets no viewer, nor does one in a collection whose id can't be read: its authorship\n\t * stays unknown.\n\t */\n\toverride async getPullRequestViewers(\n\t\tpullRequests: readonly (ProviderPullRequest | PullRequest)[],\n\t\tconnectionId?: string,\n\t): Promise<({ id: string; username?: string } | undefined)[]> {\n\t\tconst session = await this.resolveReadSessionOrThrow(connectionId, undefined);\n\t\tif (session == null) return pullRequests.map(() => undefined);\n\n\t\tconst viewerFor = this.getOrganizationViewers(session, undefined);\n\t\treturn Promise.all(\n\t\t\tpullRequests.map(pr => {\n\t\t\t\t// Azure DevOps Server's rows are provider-apis' own; only GitHub's are native.\n\t\t\t\tconst collection = isNativePullRequest(pr) ? undefined : pr.repository?.owner?.login;\n\t\t\t\treturn collection && pr.author != null ? viewerFor(collection) : Promise.resolve(undefined);\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Ignores `account`: it is the server-level one, which no collection's pull requests carry. A collection whose id\n\t * can't be read resolves `undefined`, since authorship is optional enrichment and one collection's failed lookup\n\t * must not fail the read or blank the others; it is asked once per read, as a miss isn't cached.\n\t */\n\tprotected override getOrganizationViewers(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_account: { id: string; username?: string } | undefined,\n\t): (collection: string) => Promise<{ id: string } | undefined> {\n\t\tconst viewers = new Map<string, Promise<{ id: string } | undefined>>();\n\t\treturn collection => {\n\t\t\tconst key = collection.toLowerCase();\n\t\t\tlet viewer = viewers.get(key);\n\t\t\tif (viewer == null) {\n\t\t\t\tviewer = this.getFilterUserId(session, collection).then(\n\t\t\t\t\tid => (id != null ? { id: id } : undefined),\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t\tviewers.set(key, viewer);\n\t\t\t}\n\t\t\treturn viewer;\n\t\t};\n\t}\n\n\t/** Every collection the account can see, or a refusal when discovery itself failed. */\n\tprivate async getSearchCollections(session: ProviderAuthenticationSession): Promise<AzureOrganizationDescriptor[]> {\n\t\tconst collections = await this.getProviderResourcesForUser(session);\n\t\tif (collections == null) throw this.searchUnavailable('the account’s collections could not be read');\n\n\t\treturn collections;\n\t}\n\n\t/**\n\t * A collection's projects, refused when discovery was incomplete: a search bounded by an incomplete project set\n\t * would silently omit the missing projects' matches. `refresh` re-reads them rather than trusting the discovery\n\t * cache, which lives for the whole connection and so doesn't know a project created since.\n\t */\n\tprivate async getSearchProjects(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcollection: AzureOrganizationDescriptor,\n\t\trefresh: boolean = false,\n\t): Promise<AzureProjectDescriptor[]> {\n\t\tconst projects = refresh\n\t\t\t? await this.refreshSearchProjects(session, collection)\n\t\t\t: await this.getProviderProjectsForResources(session, [collection]);\n\t\tif (projects.metadata != null && projects.metadata.completeness !== 'complete') {\n\t\t\tthrow this.searchUnavailable(`the projects of the '${collection.name}' collection could not all be read`);\n\t\t}\n\n\t\treturn projects.values;\n\t}\n\n\tprivate readonly _projectRefreshes = new Map<\n\t\tstring,\n\t\tPromise<ProviderApiCollectionResult<AzureProjectDescriptor>>\n\t>();\n\n\t/**\n\t * Re-reads a collection's projects, sharing one read among the refreshes IN FLIGHT at once — a count batch or a\n\t * multi-repository drain refreshes the same collection once per scope, and each would otherwise re-read every\n\t * project page. Nothing outlives the read, so the next search still sees a project created after it.\n\t */\n\tprivate refreshSearchProjects(\n\t\tsession: ProviderAuthenticationSession,\n\t\tcollection: AzureOrganizationDescriptor,\n\t): Promise<ProviderApiCollectionResult<AzureProjectDescriptor>> {\n\t\tconst key = `${this.discoveryKey(session)}:${collection.id}`;\n\t\tlet pending = this._projectRefreshes.get(key);\n\t\tif (pending == null) {\n\t\t\tpending = this.getProviderProjectsForResources(session, [collection], true).finally(() => {\n\t\t\t\tthis._projectRefreshes.delete(key);\n\t\t\t});\n\t\t\tthis._projectRefreshes.set(key, pending);\n\t\t}\n\n\t\treturn pending;\n\t}\n\n\t/**\n\t * Refuses dates the search can't apply before anything is read. The facade only checks that dates are\n\t * supported, not their shape, and a refusal here stays with its own scope in a count batch.\n\t */\n\tprivate assertSearchDates(criteria: { updatedAfter?: string; createdAfter?: string } | undefined): void {\n\t\tconst invalid = getInvalidAzureSearchDate(criteria);\n\t\tif (invalid != null) throw this.searchUnavailable(invalid);\n\t}\n\n\tprivate searchUnavailable(reason: string): IntegrationReadUnavailableError {\n\t\treturn new IntegrationReadUnavailableError(this.name, `search refused: ${reason}.`);\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tsuper.providerOnDisconnect();\n\t\tthis._pullRequestSearches.clear();\n\t\tthis._latestPullRequestSearches.clear();\n\t\tthis._collectionUserIds.clear();\n\t}\n\n\tprotected override async _requestForCurrentUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<Account | undefined> {\n\t\tconst azure = await this.authenticationService.apis.azure;\n\t\tconst user = azure\n\t\t\t? await azure.getCurrentUserOnServer(this, toTokenWithInfo(this.id, session), this.apiBaseUrlFor(session))\n\t\t\t: undefined;\n\t\treturn user\n\t\t\t? {\n\t\t\t\t\tprovider: this,\n\t\t\t\t\tid: user.id,\n\t\t\t\t\tname: user.name ?? undefined,\n\t\t\t\t\temail: user.email ?? undefined,\n\t\t\t\t\tavatarUrl: user.avatarUrl ?? undefined,\n\t\t\t\t\tusername: user.username ?? undefined,\n\t\t\t\t}\n\t\t\t: undefined;\n\t}\n}\n"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { CancellationError } from "../../../utils/cancellation.js";
3
3
  import { batchResults, flatSettled, mapSettledBounded, nonnullSettled } from "../../../utils/promise.js";
4
4
  import { md5 } from "../../../utils/crypto.js";
5
5
  import "../../../git/models/pullRequest.js";
6
+ import { IntegrationReadUnavailableError } from "../errors.js";
6
7
  import "../constants.js";
7
8
  import { toCollectionScopeFailure } from "../collectionMetadata.js";
8
9
  import { toTokenWithInfo } from "../authentication/models.js";
@@ -380,7 +381,7 @@ var BitbucketIntegration = class extends GitHostIntegration {
380
381
  return [...new Map(inputs.map((i) => [`${i.namespace}/${i.name}`, i])).values()];
381
382
  }
382
383
  async searchProviderMyIssues(session, repos) {
383
- if (repos == null || repos.length === 0) return void 0;
384
+ if (repos == null || repos.length === 0) throw new IntegrationReadUnavailableError(this.name, "there is no account-wide issue search — open a repository to list its issues.");
384
385
  const user = await this.getProviderCurrentAccount(session);
385
386
  if (user?.username == null) return void 0;
386
387
  const api = await this.authenticationService.apis.bitbucket;
@@ -1 +1 @@
1
- {"version":3,"file":"bitbucket.js","names":[],"sources":["../../../../../plus/integrations/src/providers/bitbucket.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScopeFailure } from '@gitkraken/provider-apis';\nimport type { Account, UnidentifiedAuthor } 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, IssueOrPullRequestType } 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 { GitRemote } from '@gitlens/git/models/remote.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { PullRequestUrlIdentity } from '@gitlens/git/utils/pullRequest.utils.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { md5 } from '@gitlens/utils/crypto.js';\nimport { uniqueBy } from '@gitlens/utils/iterable.js';\nimport { batchResults, flatSettled, mapSettledBounded, nonnullSettled } from '@gitlens/utils/promise.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type {\n\tAuthenticationSessionLike as AuthenticationSession,\n\tProviderAuthenticationSession,\n} from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport type { ProviderRefusal } from '../collectionMetadata.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { GitCloudHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport type { SearchMyPullRequestsOptions, SearchPullRequestsOptions } from '../models/gitHostIntegration.js';\nimport { GitHostIntegration } from '../models/gitHostIntegration.js';\nimport type { PullRequestEtagFields, PullRequestEtagInclude } from '../models/integration.js';\nimport { getBitbucketPullRequestIdentityFromMaybeUrl } from './bitbucket/bitbucket.utils.js';\nimport type { BitbucketRepositoryDescriptor, BitbucketWorkspaceDescriptor } from './bitbucket/models.js';\nimport { bitbucketEtagFieldsMaxIds, toBitbucketPullRequestEtagFields } from './bitbucket/models.js';\nimport type {\n\tProviderApiCollectionResult,\n\tProviderApiPagedResult,\n\tProviderHierarchyResult,\n\tProviderOrganization,\n\tProviderPullRequest,\n\tProviderRepository,\n} from './models.js';\nimport {\n\tfromProviderPullRequest,\n\tgetProviderPullRequestIdentity,\n\tprovidersMetadata,\n\tPullRequestFilter,\n\ttoProviderPullRequestStates,\n} from './models.js';\nimport {\n\tcollectProviderPagedResult,\n\tflatSettledOrThrow,\n\tmergeCollectionMetadata,\n\tparsePageCursor,\n\treadEtagFieldsByRepository,\n\ttoPageCursor,\n} from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[GitCloudHostIntegrationId.Bitbucket];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\n\nexport class BitbucketIntegration extends GitHostIntegration<\n\tGitCloudHostIntegrationId.Bitbucket,\n\tBitbucketRepositoryDescriptor\n> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\treadonly id = GitCloudHostIntegrationId.Bitbucket;\n\tprotected readonly key = this.id;\n\treadonly name: string = 'Bitbucket';\n\tget domain(): string {\n\t\treturn metadata.domain;\n\t}\n\n\tprotected get apiBaseUrl(): string {\n\t\treturn 'https://api.bitbucket.org/2.0';\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\tconst api = await this.getProvidersApi();\n\t\treturn api.mergePullRequest(toTokenWithInfo(this.id, session), pr, {\n\t\t\tmergeMethod: options?.mergeMethod,\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\tbaseUrl: this.apiBaseUrl,\n\t\t});\n\t}\n\n\tprotected override async getProviderAccountForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | UnidentifiedAuthor | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tthis.apiBaseUrl,\n\t\t\t{\n\t\t\t\tavatarSize: options?.avatarSize,\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderAccountForEmail(\n\t\t_session: AuthenticationSession,\n\t\t_repo: BitbucketRepositoryDescriptor,\n\t\t_email: string,\n\t\t_options?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\tprotected override async getProviderDefaultBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<DefaultBranch | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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{ baseUrl: this.apiBaseUrl },\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\t{ id }: { id: string; key: string },\n\t\ttype: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tid,\n\t\t\tthis.apiBaseUrl,\n\t\t\t{\n\t\t\t\ttype: type,\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:\n\t * provider-apis has no single pull request read for Bitbucket, so this uses our own.\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\t_cancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<PullRequestShape | undefined>[] | undefined> {\n\t\tconst api = await this.authenticationService.apis.bitbucket;\n\t\tif (api == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\treturn mapSettledBounded(coordinates, providerFanOutConcurrency, c =>\n\t\t\tapi.getPullRequest(this, tokenWithInfo, c.owner, c.repo, String(c.number), this.apiBaseUrl, {\n\t\t\t\tcurrentAccount: options?.currentAccount,\n\t\t\t\tdeferFailure: true,\n\t\t\t\tprojection: 'batch',\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * The cheap check behind the batch pull request read's etags: GitLens' own client lists each repository's pull\n\t * requests by id, up to {@link bitbucketEtagFieldsMaxIds} in ONE request, where\n\t * {@link getProviderPullRequestsBatch} sends one per target, and selects only the fields its rows' etags read.\n\t * Each is converted as {@link fromBitbucketPullRequest} converts the full one, so both reads compute the same\n\t * etag. A request that throws rejects only its own targets' slots.\n\t *\n\t * The full row reads neither a mergeability nor a check rollup, so its etag sees a pull request's state, draft\n\t * flag, update time and head commit, plus its participants' review decision when included; the `mergeable` and\n\t * `checks` includes add nothing and cost nothing.\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 api = await this.authenticationService.apis.bitbucket;\n\t\tif (api == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst etagIncludes = options.etagIncludes ?? [];\n\n\t\treturn readEtagFieldsByRepository(\n\t\t\tcoordinates,\n\t\t\tbitbucketEtagFieldsMaxIds,\n\t\t\t(owner, repo, ids) =>\n\t\t\t\tapi.getPullRequestsEtagFields(\n\t\t\t\t\tthis,\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\towner,\n\t\t\t\t\trepo,\n\t\t\t\t\tids,\n\t\t\t\t\tthis.apiBaseUrl,\n\t\t\t\t\t{ etagIncludes: etagIncludes, deferFailure: true },\n\t\t\t\t\tcancellation,\n\t\t\t\t),\n\t\t\tnode => toBitbucketPullRequestEtagFields(node, etagIncludes),\n\t\t);\n\t}\n\n\t/**\n\t * One request per target, settled independently, through our own client: provider-apis' pull request list has\n\t * no source-branch filter.\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\t_cancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<{ pullRequests: PullRequestShape[]; truncated: boolean }>[] | undefined> {\n\t\tconst api = await this.authenticationService.apis.bitbucket;\n\t\tif (api == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\treturn mapSettledBounded(targets, providerFanOutConcurrency, t =>\n\t\t\tapi.getPullRequestsForBranch(this, tokenWithInfo, t.owner, t.repo, t.branch, this.apiBaseUrl, {\n\t\t\t\theadOwner: t.headOwner,\n\t\t\t\tlimit: options.limit,\n\t\t\t\tcurrentAccount: options.currentAccount,\n\t\t\t\tdeferFailure: true,\n\t\t\t}),\n\t\t);\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.getIssue(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tid,\n\t\t\tthis.apiBaseUrl,\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\tbranch: string,\n\t\t_options?: {\n\t\t\tavatarSize?: number;\n\t\t\tinclude?: PullRequestState[];\n\t\t},\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tthis.apiBaseUrl,\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\trev: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tthis.apiBaseUrl,\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: BitbucketRepositoryDescriptor,\n\t\tid: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tid,\n\t\t\tthis.apiBaseUrl,\n\t\t\t{ projection: 'point' },\n\t\t);\n\t}\n\n\tprotected override getProviderPullRequestIdentityFromMaybeUrl(search: string): PullRequestUrlIdentity | undefined {\n\t\treturn getBitbucketPullRequestIdentityFromMaybeUrl(search, this.id);\n\t}\n\n\tprotected override async getProviderRepositoryMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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{ baseUrl: this.apiBaseUrl },\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate _accounts: Map<string, Account | undefined> | undefined;\n\tprotected override async getProviderCurrentAccount(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<Account | undefined> {\n\t\tconst { accessToken } = session;\n\t\tthis._accounts ??= new Map<string, Account | undefined>();\n\n\t\tconst cachedAccount = this._accounts.get(accessToken);\n\t\tif (cachedAccount == null) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst user = await api.getCurrentUser(toTokenWithInfo(this.id, session));\n\t\t\tthis._accounts.set(\n\t\t\t\taccessToken,\n\t\t\t\tuser\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tprovider: this,\n\t\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\t\tname: user.name ?? undefined,\n\t\t\t\t\t\t\temail: user.email ?? undefined,\n\t\t\t\t\t\t\tavatarUrl: user.avatarUrl ?? undefined,\n\t\t\t\t\t\t\tusername: user.username ?? undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t);\n\t\t}\n\n\t\treturn this._accounts.get(accessToken);\n\t}\n\n\t/** The profile request the account cache would otherwise answer; see `IntegrationBase.validateCredential`. */\n\tprotected override async validateCredential(session: ProviderAuthenticationSession): Promise<void> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif ((await api.getCurrentUser(toTokenWithInfo(this.id, session))) == null) {\n\t\t\tthrow new Error('Bitbucket did not confirm the credential');\n\t\t}\n\t}\n\n\t/**\n\t * Bitbucket's `403` for a token missing the OAuth scopes a request needs, which a reconnect fixes by consenting to\n\t * them again; see `IntegrationBase.isCredentialRefusal`.\n\t */\n\tprotected override isCredentialRefusal(refusal: ProviderRefusal): boolean {\n\t\treturn refusal.status === 403 && /required privilege scopes/i.test(refusal.detail ?? '');\n\t}\n\n\tprivate _workspaces: Map<string, ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined> | undefined;\n\tprivate async getProviderResourcesForCurrentUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined> {\n\t\tthis._workspaces ??= new Map<string, ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined>();\n\t\tconst { accessToken } = session;\n\t\tconst cachedResources = this._workspaces.get(accessToken);\n\t\tif (cachedResources != null && !force) return cachedResources;\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst resources = await api.getBitbucketResourcesForCurrentUser(toTokenWithInfo(this.id, session));\n\t\tif (resources == null) return undefined;\n\n\t\tconst values = resources.values.map(r => ({ ...r, key: r.id, name: r.name ?? r.slug }));\n\t\tconst metadata = mergeCollectionMetadata(\n\t\t\tresources.metadata,\n\t\t\tresources.paging?.truncated === true ? { completeness: 'partial' } : undefined,\n\t\t);\n\t\tconst result: ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> = {\n\t\t\tvalues: values,\n\t\t\t...(metadata != null ? { metadata: metadata } : {}),\n\t\t};\n\n\t\t// A partial workspace prefix is useful to the current read but must not become authoritative cache state:\n\t\t// a transient page-2 failure must be retried on the next call rather than hiding every later workspace\n\t\t// until disconnect.\n\t\tif (metadata == null || metadata.completeness === 'complete') {\n\t\t\tthis._workspaces.set(accessToken, result);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprotected override async getProviderOrganizationsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {\n\t\tconst workspaces = await this.getProviderResourcesForCurrentUser(session);\n\t\tif (workspaces == null) return undefined;\n\n\t\treturn {\n\t\t\tvalues: workspaces.values.map(w => ({\n\t\t\t\tid: w.id,\n\t\t\t\tproviderId: this.id,\n\t\t\t\tname: w.slug,\n\t\t\t\turl: `https://bitbucket.org/${w.slug}`,\n\t\t\t})),\n\t\t\t...(workspaces.metadata != null ? { metadata: workspaces.metadata } : {}),\n\t\t};\n\t}\n\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\treturn api.getReposForBitbucketWorkspace(toTokenWithInfo(this.id, session), org, {\n\t\t\tcursor: options?.cursor,\n\t\t});\n\t}\n\n\tprotected override async searchProviderMyPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: BitbucketRepositoryDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t\toptions?: SearchMyPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tif (repos != null) {\n\t\t\t// TODO: implement repos version\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst state = options?.state;\n\t\tconst states = toProviderPullRequestStates(state);\n\n\t\tconst api = await this.getProvidersApi();\n\t\tif (!api) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\t// `user.id` feeds both the authored workspace reads and the reviewer BBQL clause; guard on it so we\n\t\t// never issue requests/queries with an undefined uuid. `username` isn't used here, so don't gate on it.\n\t\tif (user?.id == null) return undefined;\n\n\t\tconst workspacesResult = await this.getProviderResourcesForCurrentUser(session);\n\t\tif (workspacesResult == null) return undefined;\n\n\t\tconst workspaces = workspacesResult.values;\n\t\tif (workspaces.length === 0) return [];\n\n\t\tconst authoredPrs = workspaces.map(async ws => {\n\t\t\tconst prs = await api.getBitbucketPullRequestsAuthoredByUserForWorkspace(\n\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\tuser.id,\n\t\t\t\tws.slug,\n\t\t\t\t{ states: states },\n\t\t\t);\n\t\t\treturn prs?.data.map(pr => fromProviderPullRequest(pr, this, { projection: 'search' }));\n\t\t});\n\n\t\tif (!options?.includeReviewRequested) {\n\t\t\treturn [\n\t\t\t\t...uniqueBy(\n\t\t\t\t\tawait flatSettled(authoredPrs),\n\t\t\t\t\tpr => pr.url,\n\t\t\t\t\t(orig, _cur) => orig,\n\t\t\t\t),\n\t\t\t];\n\t\t}\n\n\t\t// Bitbucket's reviewing PRs require a repo-scoped BBQL query; keep that sweep opt-in so the default\n\t\t// \"my pull requests\" feed stays aligned with backends that only expose authored PRs.\n\t\tconst stateClause =\n\t\t\tstate === 'closed'\n\t\t\t\t? '(state=\"DECLINED\" OR state=\"SUPERSEDED\")'\n\t\t\t\t: state === 'merged'\n\t\t\t\t\t? 'state=\"MERGED\"'\n\t\t\t\t\t: state === 'all'\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: 'state=\"OPEN\"';\n\n\t\tconst workspaceRepos = await this.getWorkspaceRepoInputs();\n\t\tif (workspaceRepos.length === 0) {\n\t\t\treturn [\n\t\t\t\t...uniqueBy(\n\t\t\t\t\tawait flatSettled(authoredPrs),\n\t\t\t\t\tpr => pr.url,\n\t\t\t\t\t(orig, _cur) => orig,\n\t\t\t\t),\n\t\t\t];\n\t\t}\n\n\t\tconst reviewerClause = `reviewers.uuid=\"${user.id}\"`;\n\t\tconst reviewingPrs = api\n\t\t\t.getPullRequestsForRepos(toTokenWithInfo(this.id, session), workspaceRepos, {\n\t\t\t\tquery: stateClause ? `${stateClause} AND ${reviewerClause}` : reviewerClause,\n\t\t\t})\n\t\t\t.then(r => r.values?.map(pr => fromProviderPullRequest(pr, this, { projection: 'search' })));\n\n\t\treturn [\n\t\t\t...uniqueBy(\n\t\t\t\tawait flatSettled([...authoredPrs, reviewingPrs]),\n\t\t\t\tpr => pr.url,\n\t\t\t\t(orig, _cur) => orig,\n\t\t\t),\n\t\t];\n\t}\n\n\tprotected override async getProviderMyPullRequestsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: {\n\t\t\tstate?: PullRequestStateFilter[];\n\t\t\tcursor?: string;\n\t\t\tincludeReviewRequested?: boolean;\n\t\t\tfilters?: PullRequestFilter[];\n\t\t},\n\t): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\tif (user?.id == null) return undefined;\n\n\t\tconst workspacesResult = await this.getProviderResourcesForCurrentUser(session);\n\t\tif (workspacesResult == null) return undefined;\n\n\t\tconst workspaces = workspacesResult.values;\n\t\tif (workspaces.length === 0) {\n\t\t\tconst incomplete =\n\t\t\t\tworkspacesResult.metadata != null && workspacesResult.metadata.completeness !== 'complete';\n\t\t\treturn {\n\t\t\t\tvalues: [],\n\t\t\t\tpaging: { cursor: '{}', more: false, truncated: incomplete || undefined },\n\t\t\t\t...(workspacesResult.metadata != null ? { metadata: workspacesResult.metadata } : {}),\n\t\t\t};\n\t\t}\n\n\t\t// Account-wide \"my PRs\" for Bitbucket = the user's authored PRs across every workspace they belong to,\n\t\t// plus the PRs they've been requested to review. Bitbucket's cross-workspace read is per-workspace and\n\t\t// numbered-page, so drain each workspace fully (bounded by a defensive backstop) and concatenate.\n\t\t// There's no single cross-workspace cursor, so the aggregate is one page; `truncated` is set when a\n\t\t// workspace hit the backstop with more pages left, or when a workspace's read was dropped by a failure.\n\t\tconst states = toProviderPullRequestStates(options?.state);\n\t\tconst requested = options?.filters?.length ? new Set(options.filters) : undefined;\n\t\tconst wantAuthored = requested == null || requested.has(PullRequestFilter.Author);\n\t\tconst wantReviewRequested =\n\t\t\trequested?.has(PullRequestFilter.ReviewRequested) ?? options?.includeReviewRequested === true;\n\t\tconst maxPagesPerWorkspace = 20;\n\t\tlet truncated = workspacesResult.metadata != null && workspacesResult.metadata.completeness !== 'complete';\n\t\t// Structured per-scope failures from BOTH the authored and reviewer slices. A rejected scope is recorded\n\t\t// here (never re-thrown) so successful sibling workspaces/repos survive and the ProviderBackend facade\n\t\t// maps each failure to an actionable warning + `fetchFailed` (auth/rate-limit failures still drive\n\t\t// recovery through their warning kind, matching the SDK's own collect-across-scopes model).\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tlet aggregateMetadata = workspacesResult.metadata;\n\t\tconst absorbCollectionResult = (result: ProviderHierarchyResult<unknown>): void => {\n\t\t\taggregateMetadata = mergeCollectionMetadata(aggregateMetadata, result.metadata);\n\t\t\tif (result.truncated || (result.metadata != null && result.metadata.completeness !== 'complete')) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t};\n\t\tconst settled = wantAuthored\n\t\t\t? await Promise.allSettled(\n\t\t\t\t\tworkspaces.map(async ws => {\n\t\t\t\t\t\treturn collectProviderPagedResult(\n\t\t\t\t\t\t\tasync cursor => {\n\t\t\t\t\t\t\t\tconst result = await api.getBitbucketPullRequestsAuthoredByUserForWorkspace(\n\t\t\t\t\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\t\t\t\tws.slug,\n\t\t\t\t\t\t\t\t\t{ states: states, page: parsePageCursor(cursor) },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (result == null) return undefined;\n\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tvalues: result.data,\n\t\t\t\t\t\t\t\t\tpaging: {\n\t\t\t\t\t\t\t\t\t\tmore: result.hasMore,\n\t\t\t\t\t\t\t\t\t\tcursor:\n\t\t\t\t\t\t\t\t\t\t\tresult.hasMore && result.nextPage != null\n\t\t\t\t\t\t\t\t\t\t\t\t? toPageCursor(result.nextPage)\n\t\t\t\t\t\t\t\t\t\t\t\t: '{}',\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmaxPagesPerWorkspace,\n\t\t\t\t\t\t\t{ providerId: this.id, resourceId: ws.slug },\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t: [];\n\n\t\tconst prsByIdentity = new Map<string, ProviderPullRequest>();\n\t\tlet unkeyedPullRequest = 0;\n\t\tconst addPullRequest = (pr: ProviderPullRequest): void => {\n\t\t\tconst key = getProviderPullRequestIdentity(pr) ?? `unkeyed:${unkeyedPullRequest++}`;\n\t\t\tif (!prsByIdentity.has(key)) {\n\t\t\t\tprsByIdentity.set(key, pr);\n\t\t\t}\n\t\t};\n\t\t// `allSettled` preserves order, so `settled[i]` is `workspaces[i]`. A rejected workspace becomes a\n\t\t// structured failure (attributed to its slug) instead of a generic truncation or a re-throw, preserving\n\t\t// every sibling workspace's authored PRs.\n\t\tsettled.forEach((outcome, i) => {\n\t\t\tif (outcome.status !== 'fulfilled') {\n\t\t\t\tfailures.push(this.toWorkspaceFailure(workspaces[i].slug, outcome.reason));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tabsorbCollectionResult(outcome.value);\n\t\t\tfor (const pr of outcome.value.values) {\n\t\t\t\taddPullRequest(pr);\n\t\t\t}\n\t\t});\n\n\t\t// Bitbucket's account-wide (per-workspace) endpoint returns authored PRs only, and the SDK exposes no\n\t\t// workspace-level reviewer query — only a repo-scoped one. Adding the reviewer slice therefore requires\n\t\t// draining every workspace's full repo list and then a per-repo `getPullRequestsForRepo` with `reviewerId`\n\t\t// (O(workspaces × repos) requests), so keep it opt-in: gate it on `includeReviewRequested` so the default\n\t\t// sweep matches the gkcli baseline (authored PRs only, no reviewer fan-out) and a caller pays that cost\n\t\t// only when it deliberately asks for review-requested PRs.\n\t\tif (wantReviewRequested) {\n\t\t\t// Enumerate the account's repos by draining each resolved workspace's repository list, then drain the\n\t\t\t// real per-repo paged `getPullRequestsForRepo` for each repo with `reviewerId`, so review-requested PRs\n\t\t\t// aren't bounded to the currently-open remotes and a reviewer PR past the first page isn't dropped.\n\t\t\t// Structured per-scope failures are collected as CollectionScopeFailure so the ProviderBackend facade\n\t\t\t// maps them to actionable warnings (Step 3).\n\t\t\tconst maxReposPagesPerWorkspace = 20;\n\t\t\tconst maxPrPagesPerRepo = 20;\n\t\t\t// Bound concurrency: workspaces are few, so process them concurrently, but drain each workspace's repos\n\t\t\t// in a bounded batch rather than an unbounded Promise.all over every repo in every workspace.\n\t\t\tconst repoBatchSize = 5;\n\n\t\t\tawait Promise.all(\n\t\t\t\tworkspaces.map(async ws => {\n\t\t\t\t\tlet repos: ProviderRepository[];\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst discovered = await collectProviderPagedResult(\n\t\t\t\t\t\t\tcursor =>\n\t\t\t\t\t\t\t\tapi.getReposForBitbucketWorkspace(toTokenWithInfo(this.id, session), ws.slug, {\n\t\t\t\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tmaxReposPagesPerWorkspace,\n\t\t\t\t\t\t\t{ providerId: this.id, resourceId: ws.slug },\n\t\t\t\t\t\t);\n\t\t\t\t\t\trepos = discovered.values;\n\t\t\t\t\t\tabsorbCollectionResult(discovered);\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t// Record the workspace as a structured failure and keep going: the failure metadata drives\n\t\t\t\t\t\t// the warning + `fetchFailed` + incompleteness for every kind (auth/rate-limit stay\n\t\t\t\t\t\t// actionable via their warning kind), so sibling workspaces' repos are still drained.\n\t\t\t\t\t\tfailures.push(this.toWorkspaceFailure(ws.slug, ex));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst drained = await batchResults(repos, repoBatchSize, async repo => {\n\t\t\t\t\t\tconst reviewing = await collectProviderPagedResult(\n\t\t\t\t\t\t\tcursor =>\n\t\t\t\t\t\t\t\tapi.getPullRequestsForRepo(\n\t\t\t\t\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\t\t\t\t\t{ namespace: repo.namespace, name: repo.name },\n\t\t\t\t\t\t\t\t\t{ reviewerId: user.id, states: states, cursor: cursor },\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tmaxPrPagesPerRepo,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tproviderId: this.id,\n\t\t\t\t\t\t\t\tresourceId: ws.slug,\n\t\t\t\t\t\t\t\trepositoryId: `${repo.namespace}/${repo.name}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn { repo: repo, reviewing: reviewing };\n\t\t\t\t\t});\n\n\t\t\t\t\tfor (let i = 0; i < drained.length; i++) {\n\t\t\t\t\t\tconst outcome = drained[i];\n\t\t\t\t\t\tif (outcome.status !== 'fulfilled') {\n\t\t\t\t\t\t\tconst repo = repos[i];\n\t\t\t\t\t\t\tfailures.push(\n\t\t\t\t\t\t\t\tthis.toRepositoryFailure(\n\t\t\t\t\t\t\t\t\t`${repo?.namespace ?? ws.slug}/${repo?.name ?? 'unknown'}`,\n\t\t\t\t\t\t\t\t\toutcome.reason,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst { reviewing } = outcome.value;\n\t\t\t\t\t\tabsorbCollectionResult(reviewing);\n\t\t\t\t\t\tfor (const pr of reviewing.values) {\n\t\t\t\t\t\t\taddPullRequest(pr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\t// Every slice that ran (authored workspaces always, reviewer workspaces/repos when opted in) records its\n\t\t// per-scope rejections as structured failures instead of re-throwing: re-throwing would discard every\n\t\t// successful sibling's PRs. Auth/rate-limit failures stay actionable through the metadata (the facade maps\n\t\t// kind `authentication` → an `auth` warning, `rate-limit` → a `rate-limit` warning) while the survivors are\n\t\t// returned with `fetchFailed`.\n\t\tconst metadata: CollectionMetadata | undefined = mergeCollectionMetadata(\n\t\t\tfailures.length > 0 ? { completeness: 'partial', failures: failures } : undefined,\n\t\t\taggregateMetadata,\n\t\t);\n\n\t\treturn {\n\t\t\tvalues: [...prsByIdentity.values()],\n\t\t\tpaging: { cursor: '{}', more: false, truncated: truncated || undefined },\n\t\t\tmetadata: metadata,\n\t\t};\n\t}\n\n\t/** Classifies a caught workspace-scope error into a structured {@link CollectionScopeFailure}. */\n\tprivate toWorkspaceFailure(workspaceSlug: string, ex: unknown): CollectionScopeFailure {\n\t\treturn toCollectionScopeFailure({ providerId: this.id, resourceId: workspaceSlug }, ex);\n\t}\n\n\t/** Classifies a caught repository-scope error into a structured {@link CollectionScopeFailure}. */\n\tprivate toRepositoryFailure(repositoryId: string, ex: unknown): CollectionScopeFailure {\n\t\treturn toCollectionScopeFailure({ providerId: this.id, repositoryId: repositoryId }, ex);\n\t}\n\n\tprotected override async searchProviderPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\tsearchQuery: string,\n\t\trepos?: BitbucketRepositoryDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst api = await this.getProvidersApi();\n\t\tif (!api) return undefined;\n\n\t\tconst workspaceRepos =\n\t\t\trepos != null\n\t\t\t\t? repos.map(r => ({ name: r.name, namespace: r.owner }))\n\t\t\t\t: await this.getWorkspaceRepoInputs();\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\t// An explicitly-empty `repos` means \"search these zero repos\" -> no results; reserve `undefined`\n\t\t// (\"scope couldn't be determined\") for when no repos were requested and none were discovered.\n\t\tif (workspaceRepos.length === 0) return repos != null ? [] : undefined;\n\n\t\tconst token = toTokenWithInfo(this.id, session);\n\t\tconst states = toProviderPullRequestStates(options?.include);\n\t\tconst providerPullRequests = await flatSettledOrThrow(\n\t\t\tworkspaceRepos.map(async repo => {\n\t\t\t\tconst result = await collectProviderPagedResult(cursor => {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\treturn api.getPullRequestsForRepo(token, repo, {\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tquery: searchQuery,\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\treturn result.values;\n\t\t\t}),\n\t\t);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\treturn providerPullRequests.map(pr => fromProviderPullRequest(pr, this, { projection: 'text-search' }));\n\t}\n\n\tprivate async getWorkspaceRepoInputs(): Promise<{ name: string; namespace: string }[]> {\n\t\tconst remotes = await this.ctx.repositories.getOpenRemotes();\n\t\tconst inputs = await nonnullSettled(\n\t\t\tremotes.map(async (r: GitRemote) => {\n\t\t\t\tconst integration = await this.authenticationService.getByRemote(r);\n\t\t\t\tif (integration !== this) return undefined;\n\n\t\t\t\t// Use the remote provider's parsing rather than splitting `r.path`, so adornments like a\n\t\t\t\t// trailing `.git` or extra path segments don't leak into the namespace/name.\n\t\t\t\tconst namespace = r.provider?.owner;\n\t\t\t\tconst name = r.provider?.repoName;\n\t\t\t\treturn namespace != null && name != null ? { name: name, namespace: namespace } : undefined;\n\t\t\t}),\n\t\t);\n\t\t// Dedupe: a repo with multiple remotes (e.g. `origin` + `upstream`) can map to the same input,\n\t\t// which would otherwise fetch and return the same PRs more than once.\n\t\treturn [...new Map(inputs.map(i => [`${i.namespace}/${i.name}`, i])).values()];\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: BitbucketRepositoryDescriptor[],\n\t): Promise<IssueShape[] | undefined> {\n\t\tif (repos == null || repos.length === 0) return undefined;\n\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\tif (user?.username == null) return undefined;\n\n\t\tconst api = await this.authenticationService.apis.bitbucket;\n\t\tif (!api) return undefined;\n\n\t\tconst issueResult = await flatSettled(\n\t\t\trepos.map(repo => {\n\t\t\t\treturn api.getUsersIssuesForRepo(\n\t\t\t\t\tthis,\n\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\tuser.id,\n\t\t\t\t\trepo.owner,\n\t\t\t\t\trepo.name,\n\t\t\t\t\tthis.apiBaseUrl,\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t\treturn issueResult;\n\t}\n\n\t/**\n\t * Bitbucket is not an issue provider on the ProviderBackend surface: Bitbucket Cloud deprecated its issue\n\t * tracker in favor of dedicated issue integrations (e.g. Jira). The legacy per-repo `getUsersIssuesForRepo`\n\t * client is retained only for autolink/hover enrichment, not for the ProviderBackend issue reads, so the\n\t * facade reports issues as unsupported rather than serving a deprecated, partial source.\n\t */\n\toverride get supportsIssues(): boolean {\n\t\treturn false;\n\t}\n\n\tprivate readonly storagePrefix = 'bitbucket';\n\tprotected override async providerOnConnect(): Promise<void> {\n\t\tif (this._session == null) return;\n\n\t\tconst accountStorageKey = md5(this._session.accessToken);\n\n\t\tconst storedAccount = this.ctx.storage.get(`${this.storagePrefix}:${accountStorageKey}:account`);\n\t\tconst storedWorkspaces = this.ctx.storage.get(`${this.storagePrefix}:${accountStorageKey}:workspaces`);\n\n\t\tlet account: Account | undefined = storedAccount?.data ? { ...storedAccount.data, provider: this } : undefined;\n\n\t\tconst storedWorkspacesData = storedWorkspaces?.data as\n\t\t\t| ProviderApiCollectionResult<BitbucketWorkspaceDescriptor>\n\t\t\t| BitbucketWorkspaceDescriptor[]\n\t\t\t| undefined;\n\t\tlet workspaces: ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined;\n\t\tif (\n\t\t\t!Array.isArray(storedWorkspacesData) &&\n\t\t\tArray.isArray(storedWorkspacesData?.values) &&\n\t\t\t(storedWorkspacesData.metadata == null || storedWorkspacesData.metadata.completeness === 'complete')\n\t\t) {\n\t\t\tworkspaces = {\n\t\t\t\tvalues: storedWorkspacesData.values.map((o: BitbucketWorkspaceDescriptor) => ({ ...o })),\n\t\t\t\t...(storedWorkspacesData.metadata != null ? { metadata: storedWorkspacesData.metadata } : {}),\n\t\t\t};\n\t\t}\n\n\t\tif (storedAccount == null) {\n\t\t\taccount = await this.getProviderCurrentAccount(this._session);\n\t\t\tif (account != null) {\n\t\t\t\t// Clear all other stored workspaces and repositories and accounts when our session changes\n\t\t\t\tawait this.ctx.storage.deleteWithPrefix(this.storagePrefix);\n\t\t\t\tawait this.ctx.storage.store(`${this.storagePrefix}:${accountStorageKey}:account`, {\n\t\t\t\t\tv: 1,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tid: account.id,\n\t\t\t\t\t\tname: account.name,\n\t\t\t\t\t\temail: account.email,\n\t\t\t\t\t\tavatarUrl: account.avatarUrl,\n\t\t\t\t\t\tusername: account.username,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis._accounts ??= new Map<string, Account | undefined>();\n\t\tthis._accounts.set(this._session.accessToken, account);\n\n\t\tif (workspaces == null) {\n\t\t\tconst discovered = await this.getProviderResourcesForCurrentUser(this._session, true);\n\t\t\tif (discovered?.metadata == null || discovered.metadata.completeness === 'complete') {\n\t\t\t\tworkspaces = discovered;\n\t\t\t\tif (workspaces != null) {\n\t\t\t\t\tawait this.ctx.storage.store(`${this.storagePrefix}:${accountStorageKey}:workspaces`, {\n\t\t\t\t\t\tv: 2,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\tdata: workspaces,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (workspaces != null) {\n\t\t\tthis._workspaces ??= new Map<\n\t\t\t\tstring,\n\t\t\t\tProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined\n\t\t\t>();\n\t\t\tthis._workspaces.set(this._session.accessToken, workspaces);\n\t\t}\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tthis._accounts = undefined;\n\t\tthis._workspaces = undefined;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bitbucket.js","names":[],"sources":["../../../../../plus/integrations/src/providers/bitbucket.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScopeFailure } from '@gitkraken/provider-apis';\nimport type { Account, UnidentifiedAuthor } 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, IssueOrPullRequestType } 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 { GitRemote } from '@gitlens/git/models/remote.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { PullRequestUrlIdentity } from '@gitlens/git/utils/pullRequest.utils.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { md5 } from '@gitlens/utils/crypto.js';\nimport { uniqueBy } from '@gitlens/utils/iterable.js';\nimport { batchResults, flatSettled, mapSettledBounded, nonnullSettled } from '@gitlens/utils/promise.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type {\n\tAuthenticationSessionLike as AuthenticationSession,\n\tProviderAuthenticationSession,\n} from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport type { ProviderRefusal } from '../collectionMetadata.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { GitCloudHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport { IntegrationReadUnavailableError } from '../errors.js';\nimport type { SearchMyPullRequestsOptions, SearchPullRequestsOptions } from '../models/gitHostIntegration.js';\nimport { GitHostIntegration } from '../models/gitHostIntegration.js';\nimport type { PullRequestEtagFields, PullRequestEtagInclude } from '../models/integration.js';\nimport { getBitbucketPullRequestIdentityFromMaybeUrl } from './bitbucket/bitbucket.utils.js';\nimport type { BitbucketRepositoryDescriptor, BitbucketWorkspaceDescriptor } from './bitbucket/models.js';\nimport { bitbucketEtagFieldsMaxIds, toBitbucketPullRequestEtagFields } from './bitbucket/models.js';\nimport type {\n\tProviderApiCollectionResult,\n\tProviderApiPagedResult,\n\tProviderHierarchyResult,\n\tProviderOrganization,\n\tProviderPullRequest,\n\tProviderRepository,\n} from './models.js';\nimport {\n\tfromProviderPullRequest,\n\tgetProviderPullRequestIdentity,\n\tprovidersMetadata,\n\tPullRequestFilter,\n\ttoProviderPullRequestStates,\n} from './models.js';\nimport {\n\tcollectProviderPagedResult,\n\tflatSettledOrThrow,\n\tmergeCollectionMetadata,\n\tparsePageCursor,\n\treadEtagFieldsByRepository,\n\ttoPageCursor,\n} from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[GitCloudHostIntegrationId.Bitbucket];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\n\nexport class BitbucketIntegration extends GitHostIntegration<\n\tGitCloudHostIntegrationId.Bitbucket,\n\tBitbucketRepositoryDescriptor\n> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\treadonly id = GitCloudHostIntegrationId.Bitbucket;\n\tprotected readonly key = this.id;\n\treadonly name: string = 'Bitbucket';\n\tget domain(): string {\n\t\treturn metadata.domain;\n\t}\n\n\tprotected get apiBaseUrl(): string {\n\t\treturn 'https://api.bitbucket.org/2.0';\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\tconst api = await this.getProvidersApi();\n\t\treturn api.mergePullRequest(toTokenWithInfo(this.id, session), pr, {\n\t\t\tmergeMethod: options?.mergeMethod,\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\tbaseUrl: this.apiBaseUrl,\n\t\t});\n\t}\n\n\tprotected override async getProviderAccountForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | UnidentifiedAuthor | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tthis.apiBaseUrl,\n\t\t\t{\n\t\t\t\tavatarSize: options?.avatarSize,\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderAccountForEmail(\n\t\t_session: AuthenticationSession,\n\t\t_repo: BitbucketRepositoryDescriptor,\n\t\t_email: string,\n\t\t_options?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\tprotected override async getProviderDefaultBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<DefaultBranch | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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{ baseUrl: this.apiBaseUrl },\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\t{ id }: { id: string; key: string },\n\t\ttype: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tid,\n\t\t\tthis.apiBaseUrl,\n\t\t\t{\n\t\t\t\ttype: type,\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:\n\t * provider-apis has no single pull request read for Bitbucket, so this uses our own.\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\t_cancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<PullRequestShape | undefined>[] | undefined> {\n\t\tconst api = await this.authenticationService.apis.bitbucket;\n\t\tif (api == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\treturn mapSettledBounded(coordinates, providerFanOutConcurrency, c =>\n\t\t\tapi.getPullRequest(this, tokenWithInfo, c.owner, c.repo, String(c.number), this.apiBaseUrl, {\n\t\t\t\tcurrentAccount: options?.currentAccount,\n\t\t\t\tdeferFailure: true,\n\t\t\t\tprojection: 'batch',\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * The cheap check behind the batch pull request read's etags: GitLens' own client lists each repository's pull\n\t * requests by id, up to {@link bitbucketEtagFieldsMaxIds} in ONE request, where\n\t * {@link getProviderPullRequestsBatch} sends one per target, and selects only the fields its rows' etags read.\n\t * Each is converted as {@link fromBitbucketPullRequest} converts the full one, so both reads compute the same\n\t * etag. A request that throws rejects only its own targets' slots.\n\t *\n\t * The full row reads neither a mergeability nor a check rollup, so its etag sees a pull request's state, draft\n\t * flag, update time and head commit, plus its participants' review decision when included; the `mergeable` and\n\t * `checks` includes add nothing and cost nothing.\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 api = await this.authenticationService.apis.bitbucket;\n\t\tif (api == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst etagIncludes = options.etagIncludes ?? [];\n\n\t\treturn readEtagFieldsByRepository(\n\t\t\tcoordinates,\n\t\t\tbitbucketEtagFieldsMaxIds,\n\t\t\t(owner, repo, ids) =>\n\t\t\t\tapi.getPullRequestsEtagFields(\n\t\t\t\t\tthis,\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\towner,\n\t\t\t\t\trepo,\n\t\t\t\t\tids,\n\t\t\t\t\tthis.apiBaseUrl,\n\t\t\t\t\t{ etagIncludes: etagIncludes, deferFailure: true },\n\t\t\t\t\tcancellation,\n\t\t\t\t),\n\t\t\tnode => toBitbucketPullRequestEtagFields(node, etagIncludes),\n\t\t);\n\t}\n\n\t/**\n\t * One request per target, settled independently, through our own client: provider-apis' pull request list has\n\t * no source-branch filter.\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\t_cancellation?: AbortSignal,\n\t): Promise<PromiseSettledResult<{ pullRequests: PullRequestShape[]; truncated: boolean }>[] | undefined> {\n\t\tconst api = await this.authenticationService.apis.bitbucket;\n\t\tif (api == null) return undefined;\n\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\treturn mapSettledBounded(targets, providerFanOutConcurrency, t =>\n\t\t\tapi.getPullRequestsForBranch(this, tokenWithInfo, t.owner, t.repo, t.branch, this.apiBaseUrl, {\n\t\t\t\theadOwner: t.headOwner,\n\t\t\t\tlimit: options.limit,\n\t\t\t\tcurrentAccount: options.currentAccount,\n\t\t\t\tdeferFailure: true,\n\t\t\t}),\n\t\t);\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.getIssue(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tid,\n\t\t\tthis.apiBaseUrl,\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\tbranch: string,\n\t\t_options?: {\n\t\t\tavatarSize?: number;\n\t\t\tinclude?: PullRequestState[];\n\t\t},\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tthis.apiBaseUrl,\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\trev: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tthis.apiBaseUrl,\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: BitbucketRepositoryDescriptor,\n\t\tid: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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\tid,\n\t\t\tthis.apiBaseUrl,\n\t\t\t{ projection: 'point' },\n\t\t);\n\t}\n\n\tprotected override getProviderPullRequestIdentityFromMaybeUrl(search: string): PullRequestUrlIdentity | undefined {\n\t\treturn getBitbucketPullRequestIdentityFromMaybeUrl(search, this.id);\n\t}\n\n\tprotected override async getProviderRepositoryMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: BitbucketRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\treturn (await this.authenticationService.apis.bitbucket)?.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{ baseUrl: this.apiBaseUrl },\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate _accounts: Map<string, Account | undefined> | undefined;\n\tprotected override async getProviderCurrentAccount(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<Account | undefined> {\n\t\tconst { accessToken } = session;\n\t\tthis._accounts ??= new Map<string, Account | undefined>();\n\n\t\tconst cachedAccount = this._accounts.get(accessToken);\n\t\tif (cachedAccount == null) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst user = await api.getCurrentUser(toTokenWithInfo(this.id, session));\n\t\t\tthis._accounts.set(\n\t\t\t\taccessToken,\n\t\t\t\tuser\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tprovider: this,\n\t\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\t\tname: user.name ?? undefined,\n\t\t\t\t\t\t\temail: user.email ?? undefined,\n\t\t\t\t\t\t\tavatarUrl: user.avatarUrl ?? undefined,\n\t\t\t\t\t\t\tusername: user.username ?? undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t);\n\t\t}\n\n\t\treturn this._accounts.get(accessToken);\n\t}\n\n\t/** The profile request the account cache would otherwise answer; see `IntegrationBase.validateCredential`. */\n\tprotected override async validateCredential(session: ProviderAuthenticationSession): Promise<void> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif ((await api.getCurrentUser(toTokenWithInfo(this.id, session))) == null) {\n\t\t\tthrow new Error('Bitbucket did not confirm the credential');\n\t\t}\n\t}\n\n\t/**\n\t * Bitbucket's `403` for a token missing the OAuth scopes a request needs, which a reconnect fixes by consenting to\n\t * them again; see `IntegrationBase.isCredentialRefusal`.\n\t */\n\tprotected override isCredentialRefusal(refusal: ProviderRefusal): boolean {\n\t\treturn refusal.status === 403 && /required privilege scopes/i.test(refusal.detail ?? '');\n\t}\n\n\tprivate _workspaces: Map<string, ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined> | undefined;\n\tprivate async getProviderResourcesForCurrentUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined> {\n\t\tthis._workspaces ??= new Map<string, ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined>();\n\t\tconst { accessToken } = session;\n\t\tconst cachedResources = this._workspaces.get(accessToken);\n\t\tif (cachedResources != null && !force) return cachedResources;\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst resources = await api.getBitbucketResourcesForCurrentUser(toTokenWithInfo(this.id, session));\n\t\tif (resources == null) return undefined;\n\n\t\tconst values = resources.values.map(r => ({ ...r, key: r.id, name: r.name ?? r.slug }));\n\t\tconst metadata = mergeCollectionMetadata(\n\t\t\tresources.metadata,\n\t\t\tresources.paging?.truncated === true ? { completeness: 'partial' } : undefined,\n\t\t);\n\t\tconst result: ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> = {\n\t\t\tvalues: values,\n\t\t\t...(metadata != null ? { metadata: metadata } : {}),\n\t\t};\n\n\t\t// A partial workspace prefix is useful to the current read but must not become authoritative cache state:\n\t\t// a transient page-2 failure must be retried on the next call rather than hiding every later workspace\n\t\t// until disconnect.\n\t\tif (metadata == null || metadata.completeness === 'complete') {\n\t\t\tthis._workspaces.set(accessToken, result);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprotected override async getProviderOrganizationsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {\n\t\tconst workspaces = await this.getProviderResourcesForCurrentUser(session);\n\t\tif (workspaces == null) return undefined;\n\n\t\treturn {\n\t\t\tvalues: workspaces.values.map(w => ({\n\t\t\t\tid: w.id,\n\t\t\t\tproviderId: this.id,\n\t\t\t\tname: w.slug,\n\t\t\t\turl: `https://bitbucket.org/${w.slug}`,\n\t\t\t})),\n\t\t\t...(workspaces.metadata != null ? { metadata: workspaces.metadata } : {}),\n\t\t};\n\t}\n\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\treturn api.getReposForBitbucketWorkspace(toTokenWithInfo(this.id, session), org, {\n\t\t\tcursor: options?.cursor,\n\t\t});\n\t}\n\n\tprotected override async searchProviderMyPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: BitbucketRepositoryDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t\toptions?: SearchMyPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tif (repos != null) {\n\t\t\t// TODO: implement repos version\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst state = options?.state;\n\t\tconst states = toProviderPullRequestStates(state);\n\n\t\tconst api = await this.getProvidersApi();\n\t\tif (!api) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\t// `user.id` feeds both the authored workspace reads and the reviewer BBQL clause; guard on it so we\n\t\t// never issue requests/queries with an undefined uuid. `username` isn't used here, so don't gate on it.\n\t\tif (user?.id == null) return undefined;\n\n\t\tconst workspacesResult = await this.getProviderResourcesForCurrentUser(session);\n\t\tif (workspacesResult == null) return undefined;\n\n\t\tconst workspaces = workspacesResult.values;\n\t\tif (workspaces.length === 0) return [];\n\n\t\tconst authoredPrs = workspaces.map(async ws => {\n\t\t\tconst prs = await api.getBitbucketPullRequestsAuthoredByUserForWorkspace(\n\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\tuser.id,\n\t\t\t\tws.slug,\n\t\t\t\t{ states: states },\n\t\t\t);\n\t\t\treturn prs?.data.map(pr => fromProviderPullRequest(pr, this, { projection: 'search' }));\n\t\t});\n\n\t\tif (!options?.includeReviewRequested) {\n\t\t\treturn [\n\t\t\t\t...uniqueBy(\n\t\t\t\t\tawait flatSettled(authoredPrs),\n\t\t\t\t\tpr => pr.url,\n\t\t\t\t\t(orig, _cur) => orig,\n\t\t\t\t),\n\t\t\t];\n\t\t}\n\n\t\t// Bitbucket's reviewing PRs require a repo-scoped BBQL query; keep that sweep opt-in so the default\n\t\t// \"my pull requests\" feed stays aligned with backends that only expose authored PRs.\n\t\tconst stateClause =\n\t\t\tstate === 'closed'\n\t\t\t\t? '(state=\"DECLINED\" OR state=\"SUPERSEDED\")'\n\t\t\t\t: state === 'merged'\n\t\t\t\t\t? 'state=\"MERGED\"'\n\t\t\t\t\t: state === 'all'\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: 'state=\"OPEN\"';\n\n\t\tconst workspaceRepos = await this.getWorkspaceRepoInputs();\n\t\tif (workspaceRepos.length === 0) {\n\t\t\treturn [\n\t\t\t\t...uniqueBy(\n\t\t\t\t\tawait flatSettled(authoredPrs),\n\t\t\t\t\tpr => pr.url,\n\t\t\t\t\t(orig, _cur) => orig,\n\t\t\t\t),\n\t\t\t];\n\t\t}\n\n\t\tconst reviewerClause = `reviewers.uuid=\"${user.id}\"`;\n\t\tconst reviewingPrs = api\n\t\t\t.getPullRequestsForRepos(toTokenWithInfo(this.id, session), workspaceRepos, {\n\t\t\t\tquery: stateClause ? `${stateClause} AND ${reviewerClause}` : reviewerClause,\n\t\t\t})\n\t\t\t.then(r => r.values?.map(pr => fromProviderPullRequest(pr, this, { projection: 'search' })));\n\n\t\treturn [\n\t\t\t...uniqueBy(\n\t\t\t\tawait flatSettled([...authoredPrs, reviewingPrs]),\n\t\t\t\tpr => pr.url,\n\t\t\t\t(orig, _cur) => orig,\n\t\t\t),\n\t\t];\n\t}\n\n\tprotected override async getProviderMyPullRequestsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: {\n\t\t\tstate?: PullRequestStateFilter[];\n\t\t\tcursor?: string;\n\t\t\tincludeReviewRequested?: boolean;\n\t\t\tfilters?: PullRequestFilter[];\n\t\t},\n\t): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\tif (user?.id == null) return undefined;\n\n\t\tconst workspacesResult = await this.getProviderResourcesForCurrentUser(session);\n\t\tif (workspacesResult == null) return undefined;\n\n\t\tconst workspaces = workspacesResult.values;\n\t\tif (workspaces.length === 0) {\n\t\t\tconst incomplete =\n\t\t\t\tworkspacesResult.metadata != null && workspacesResult.metadata.completeness !== 'complete';\n\t\t\treturn {\n\t\t\t\tvalues: [],\n\t\t\t\tpaging: { cursor: '{}', more: false, truncated: incomplete || undefined },\n\t\t\t\t...(workspacesResult.metadata != null ? { metadata: workspacesResult.metadata } : {}),\n\t\t\t};\n\t\t}\n\n\t\t// Account-wide \"my PRs\" for Bitbucket = the user's authored PRs across every workspace they belong to,\n\t\t// plus the PRs they've been requested to review. Bitbucket's cross-workspace read is per-workspace and\n\t\t// numbered-page, so drain each workspace fully (bounded by a defensive backstop) and concatenate.\n\t\t// There's no single cross-workspace cursor, so the aggregate is one page; `truncated` is set when a\n\t\t// workspace hit the backstop with more pages left, or when a workspace's read was dropped by a failure.\n\t\tconst states = toProviderPullRequestStates(options?.state);\n\t\tconst requested = options?.filters?.length ? new Set(options.filters) : undefined;\n\t\tconst wantAuthored = requested == null || requested.has(PullRequestFilter.Author);\n\t\tconst wantReviewRequested =\n\t\t\trequested?.has(PullRequestFilter.ReviewRequested) ?? options?.includeReviewRequested === true;\n\t\tconst maxPagesPerWorkspace = 20;\n\t\tlet truncated = workspacesResult.metadata != null && workspacesResult.metadata.completeness !== 'complete';\n\t\t// Structured per-scope failures from BOTH the authored and reviewer slices. A rejected scope is recorded\n\t\t// here (never re-thrown) so successful sibling workspaces/repos survive and the ProviderBackend facade\n\t\t// maps each failure to an actionable warning + `fetchFailed` (auth/rate-limit failures still drive\n\t\t// recovery through their warning kind, matching the SDK's own collect-across-scopes model).\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tlet aggregateMetadata = workspacesResult.metadata;\n\t\tconst absorbCollectionResult = (result: ProviderHierarchyResult<unknown>): void => {\n\t\t\taggregateMetadata = mergeCollectionMetadata(aggregateMetadata, result.metadata);\n\t\t\tif (result.truncated || (result.metadata != null && result.metadata.completeness !== 'complete')) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t};\n\t\tconst settled = wantAuthored\n\t\t\t? await Promise.allSettled(\n\t\t\t\t\tworkspaces.map(async ws => {\n\t\t\t\t\t\treturn collectProviderPagedResult(\n\t\t\t\t\t\t\tasync cursor => {\n\t\t\t\t\t\t\t\tconst result = await api.getBitbucketPullRequestsAuthoredByUserForWorkspace(\n\t\t\t\t\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\t\t\t\tws.slug,\n\t\t\t\t\t\t\t\t\t{ states: states, page: parsePageCursor(cursor) },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (result == null) return undefined;\n\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tvalues: result.data,\n\t\t\t\t\t\t\t\t\tpaging: {\n\t\t\t\t\t\t\t\t\t\tmore: result.hasMore,\n\t\t\t\t\t\t\t\t\t\tcursor:\n\t\t\t\t\t\t\t\t\t\t\tresult.hasMore && result.nextPage != null\n\t\t\t\t\t\t\t\t\t\t\t\t? toPageCursor(result.nextPage)\n\t\t\t\t\t\t\t\t\t\t\t\t: '{}',\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmaxPagesPerWorkspace,\n\t\t\t\t\t\t\t{ providerId: this.id, resourceId: ws.slug },\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t: [];\n\n\t\tconst prsByIdentity = new Map<string, ProviderPullRequest>();\n\t\tlet unkeyedPullRequest = 0;\n\t\tconst addPullRequest = (pr: ProviderPullRequest): void => {\n\t\t\tconst key = getProviderPullRequestIdentity(pr) ?? `unkeyed:${unkeyedPullRequest++}`;\n\t\t\tif (!prsByIdentity.has(key)) {\n\t\t\t\tprsByIdentity.set(key, pr);\n\t\t\t}\n\t\t};\n\t\t// `allSettled` preserves order, so `settled[i]` is `workspaces[i]`. A rejected workspace becomes a\n\t\t// structured failure (attributed to its slug) instead of a generic truncation or a re-throw, preserving\n\t\t// every sibling workspace's authored PRs.\n\t\tsettled.forEach((outcome, i) => {\n\t\t\tif (outcome.status !== 'fulfilled') {\n\t\t\t\tfailures.push(this.toWorkspaceFailure(workspaces[i].slug, outcome.reason));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tabsorbCollectionResult(outcome.value);\n\t\t\tfor (const pr of outcome.value.values) {\n\t\t\t\taddPullRequest(pr);\n\t\t\t}\n\t\t});\n\n\t\t// Bitbucket's account-wide (per-workspace) endpoint returns authored PRs only, and the SDK exposes no\n\t\t// workspace-level reviewer query — only a repo-scoped one. Adding the reviewer slice therefore requires\n\t\t// draining every workspace's full repo list and then a per-repo `getPullRequestsForRepo` with `reviewerId`\n\t\t// (O(workspaces × repos) requests), so keep it opt-in: gate it on `includeReviewRequested` so the default\n\t\t// sweep matches the gkcli baseline (authored PRs only, no reviewer fan-out) and a caller pays that cost\n\t\t// only when it deliberately asks for review-requested PRs.\n\t\tif (wantReviewRequested) {\n\t\t\t// Enumerate the account's repos by draining each resolved workspace's repository list, then drain the\n\t\t\t// real per-repo paged `getPullRequestsForRepo` for each repo with `reviewerId`, so review-requested PRs\n\t\t\t// aren't bounded to the currently-open remotes and a reviewer PR past the first page isn't dropped.\n\t\t\t// Structured per-scope failures are collected as CollectionScopeFailure so the ProviderBackend facade\n\t\t\t// maps them to actionable warnings (Step 3).\n\t\t\tconst maxReposPagesPerWorkspace = 20;\n\t\t\tconst maxPrPagesPerRepo = 20;\n\t\t\t// Bound concurrency: workspaces are few, so process them concurrently, but drain each workspace's repos\n\t\t\t// in a bounded batch rather than an unbounded Promise.all over every repo in every workspace.\n\t\t\tconst repoBatchSize = 5;\n\n\t\t\tawait Promise.all(\n\t\t\t\tworkspaces.map(async ws => {\n\t\t\t\t\tlet repos: ProviderRepository[];\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst discovered = await collectProviderPagedResult(\n\t\t\t\t\t\t\tcursor =>\n\t\t\t\t\t\t\t\tapi.getReposForBitbucketWorkspace(toTokenWithInfo(this.id, session), ws.slug, {\n\t\t\t\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tmaxReposPagesPerWorkspace,\n\t\t\t\t\t\t\t{ providerId: this.id, resourceId: ws.slug },\n\t\t\t\t\t\t);\n\t\t\t\t\t\trepos = discovered.values;\n\t\t\t\t\t\tabsorbCollectionResult(discovered);\n\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t// Record the workspace as a structured failure and keep going: the failure metadata drives\n\t\t\t\t\t\t// the warning + `fetchFailed` + incompleteness for every kind (auth/rate-limit stay\n\t\t\t\t\t\t// actionable via their warning kind), so sibling workspaces' repos are still drained.\n\t\t\t\t\t\tfailures.push(this.toWorkspaceFailure(ws.slug, ex));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst drained = await batchResults(repos, repoBatchSize, async repo => {\n\t\t\t\t\t\tconst reviewing = await collectProviderPagedResult(\n\t\t\t\t\t\t\tcursor =>\n\t\t\t\t\t\t\t\tapi.getPullRequestsForRepo(\n\t\t\t\t\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\t\t\t\t\t{ namespace: repo.namespace, name: repo.name },\n\t\t\t\t\t\t\t\t\t{ reviewerId: user.id, states: states, cursor: cursor },\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tmaxPrPagesPerRepo,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tproviderId: this.id,\n\t\t\t\t\t\t\t\tresourceId: ws.slug,\n\t\t\t\t\t\t\t\trepositoryId: `${repo.namespace}/${repo.name}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn { repo: repo, reviewing: reviewing };\n\t\t\t\t\t});\n\n\t\t\t\t\tfor (let i = 0; i < drained.length; i++) {\n\t\t\t\t\t\tconst outcome = drained[i];\n\t\t\t\t\t\tif (outcome.status !== 'fulfilled') {\n\t\t\t\t\t\t\tconst repo = repos[i];\n\t\t\t\t\t\t\tfailures.push(\n\t\t\t\t\t\t\t\tthis.toRepositoryFailure(\n\t\t\t\t\t\t\t\t\t`${repo?.namespace ?? ws.slug}/${repo?.name ?? 'unknown'}`,\n\t\t\t\t\t\t\t\t\toutcome.reason,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst { reviewing } = outcome.value;\n\t\t\t\t\t\tabsorbCollectionResult(reviewing);\n\t\t\t\t\t\tfor (const pr of reviewing.values) {\n\t\t\t\t\t\t\taddPullRequest(pr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\t// Every slice that ran (authored workspaces always, reviewer workspaces/repos when opted in) records its\n\t\t// per-scope rejections as structured failures instead of re-throwing: re-throwing would discard every\n\t\t// successful sibling's PRs. Auth/rate-limit failures stay actionable through the metadata (the facade maps\n\t\t// kind `authentication` → an `auth` warning, `rate-limit` → a `rate-limit` warning) while the survivors are\n\t\t// returned with `fetchFailed`.\n\t\tconst metadata: CollectionMetadata | undefined = mergeCollectionMetadata(\n\t\t\tfailures.length > 0 ? { completeness: 'partial', failures: failures } : undefined,\n\t\t\taggregateMetadata,\n\t\t);\n\n\t\treturn {\n\t\t\tvalues: [...prsByIdentity.values()],\n\t\t\tpaging: { cursor: '{}', more: false, truncated: truncated || undefined },\n\t\t\tmetadata: metadata,\n\t\t};\n\t}\n\n\t/** Classifies a caught workspace-scope error into a structured {@link CollectionScopeFailure}. */\n\tprivate toWorkspaceFailure(workspaceSlug: string, ex: unknown): CollectionScopeFailure {\n\t\treturn toCollectionScopeFailure({ providerId: this.id, resourceId: workspaceSlug }, ex);\n\t}\n\n\t/** Classifies a caught repository-scope error into a structured {@link CollectionScopeFailure}. */\n\tprivate toRepositoryFailure(repositoryId: string, ex: unknown): CollectionScopeFailure {\n\t\treturn toCollectionScopeFailure({ providerId: this.id, repositoryId: repositoryId }, ex);\n\t}\n\n\tprotected override async searchProviderPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\tsearchQuery: string,\n\t\trepos?: BitbucketRepositoryDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst api = await this.getProvidersApi();\n\t\tif (!api) return undefined;\n\n\t\tconst workspaceRepos =\n\t\t\trepos != null\n\t\t\t\t? repos.map(r => ({ name: r.name, namespace: r.owner }))\n\t\t\t\t: await this.getWorkspaceRepoInputs();\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\t// An explicitly-empty `repos` means \"search these zero repos\" -> no results; reserve `undefined`\n\t\t// (\"scope couldn't be determined\") for when no repos were requested and none were discovered.\n\t\tif (workspaceRepos.length === 0) return repos != null ? [] : undefined;\n\n\t\tconst token = toTokenWithInfo(this.id, session);\n\t\tconst states = toProviderPullRequestStates(options?.include);\n\t\tconst providerPullRequests = await flatSettledOrThrow(\n\t\t\tworkspaceRepos.map(async repo => {\n\t\t\t\tconst result = await collectProviderPagedResult(cursor => {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\treturn api.getPullRequestsForRepo(token, repo, {\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tquery: searchQuery,\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\treturn result.values;\n\t\t\t}),\n\t\t);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\treturn providerPullRequests.map(pr => fromProviderPullRequest(pr, this, { projection: 'text-search' }));\n\t}\n\n\tprivate async getWorkspaceRepoInputs(): Promise<{ name: string; namespace: string }[]> {\n\t\tconst remotes = await this.ctx.repositories.getOpenRemotes();\n\t\tconst inputs = await nonnullSettled(\n\t\t\tremotes.map(async (r: GitRemote) => {\n\t\t\t\tconst integration = await this.authenticationService.getByRemote(r);\n\t\t\t\tif (integration !== this) return undefined;\n\n\t\t\t\t// Use the remote provider's parsing rather than splitting `r.path`, so adornments like a\n\t\t\t\t// trailing `.git` or extra path segments don't leak into the namespace/name.\n\t\t\t\tconst namespace = r.provider?.owner;\n\t\t\t\tconst name = r.provider?.repoName;\n\t\t\t\treturn namespace != null && name != null ? { name: name, namespace: namespace } : undefined;\n\t\t\t}),\n\t\t);\n\t\t// Dedupe: a repo with multiple remotes (e.g. `origin` + `upstream`) can map to the same input,\n\t\t// which would otherwise fetch and return the same PRs more than once.\n\t\treturn [...new Map(inputs.map(i => [`${i.namespace}/${i.name}`, i])).values()];\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: BitbucketRepositoryDescriptor[],\n\t): Promise<IssueShape[] | undefined> {\n\t\t// Bitbucket Cloud's legacy issues API is per-repo only — there is no account-wide search endpoint to\n\t\t// call, so an unscoped read can't be served. Returning undefined here would be indistinguishable from a\n\t\t// genuinely empty account; throw so the caller can surface this as a warning instead.\n\t\tif (repos == null || repos.length === 0) {\n\t\t\tthrow new IntegrationReadUnavailableError(\n\t\t\t\tthis.name,\n\t\t\t\t'there is no account-wide issue search — open a repository to list its issues.',\n\t\t\t);\n\t\t}\n\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\tif (user?.username == null) return undefined;\n\n\t\tconst api = await this.authenticationService.apis.bitbucket;\n\t\tif (!api) return undefined;\n\n\t\tconst issueResult = await flatSettled(\n\t\t\trepos.map(repo => {\n\t\t\t\treturn api.getUsersIssuesForRepo(\n\t\t\t\t\tthis,\n\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\tuser.id,\n\t\t\t\t\trepo.owner,\n\t\t\t\t\trepo.name,\n\t\t\t\t\tthis.apiBaseUrl,\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t\treturn issueResult;\n\t}\n\n\t/**\n\t * Bitbucket is not an issue provider on the ProviderBackend surface: Bitbucket Cloud deprecated its issue\n\t * tracker in favor of dedicated issue integrations (e.g. Jira). The legacy per-repo `getUsersIssuesForRepo`\n\t * client is retained only for autolink/hover enrichment, not for the ProviderBackend issue reads, so the\n\t * facade reports issues as unsupported rather than serving a deprecated, partial source.\n\t */\n\toverride get supportsIssues(): boolean {\n\t\treturn false;\n\t}\n\n\tprivate readonly storagePrefix = 'bitbucket';\n\tprotected override async providerOnConnect(): Promise<void> {\n\t\tif (this._session == null) return;\n\n\t\tconst accountStorageKey = md5(this._session.accessToken);\n\n\t\tconst storedAccount = this.ctx.storage.get(`${this.storagePrefix}:${accountStorageKey}:account`);\n\t\tconst storedWorkspaces = this.ctx.storage.get(`${this.storagePrefix}:${accountStorageKey}:workspaces`);\n\n\t\tlet account: Account | undefined = storedAccount?.data ? { ...storedAccount.data, provider: this } : undefined;\n\n\t\tconst storedWorkspacesData = storedWorkspaces?.data as\n\t\t\t| ProviderApiCollectionResult<BitbucketWorkspaceDescriptor>\n\t\t\t| BitbucketWorkspaceDescriptor[]\n\t\t\t| undefined;\n\t\tlet workspaces: ProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined;\n\t\tif (\n\t\t\t!Array.isArray(storedWorkspacesData) &&\n\t\t\tArray.isArray(storedWorkspacesData?.values) &&\n\t\t\t(storedWorkspacesData.metadata == null || storedWorkspacesData.metadata.completeness === 'complete')\n\t\t) {\n\t\t\tworkspaces = {\n\t\t\t\tvalues: storedWorkspacesData.values.map((o: BitbucketWorkspaceDescriptor) => ({ ...o })),\n\t\t\t\t...(storedWorkspacesData.metadata != null ? { metadata: storedWorkspacesData.metadata } : {}),\n\t\t\t};\n\t\t}\n\n\t\tif (storedAccount == null) {\n\t\t\taccount = await this.getProviderCurrentAccount(this._session);\n\t\t\tif (account != null) {\n\t\t\t\t// Clear all other stored workspaces and repositories and accounts when our session changes\n\t\t\t\tawait this.ctx.storage.deleteWithPrefix(this.storagePrefix);\n\t\t\t\tawait this.ctx.storage.store(`${this.storagePrefix}:${accountStorageKey}:account`, {\n\t\t\t\t\tv: 1,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tid: account.id,\n\t\t\t\t\t\tname: account.name,\n\t\t\t\t\t\temail: account.email,\n\t\t\t\t\t\tavatarUrl: account.avatarUrl,\n\t\t\t\t\t\tusername: account.username,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis._accounts ??= new Map<string, Account | undefined>();\n\t\tthis._accounts.set(this._session.accessToken, account);\n\n\t\tif (workspaces == null) {\n\t\t\tconst discovered = await this.getProviderResourcesForCurrentUser(this._session, true);\n\t\t\tif (discovered?.metadata == null || discovered.metadata.completeness === 'complete') {\n\t\t\t\tworkspaces = discovered;\n\t\t\t\tif (workspaces != null) {\n\t\t\t\t\tawait this.ctx.storage.store(`${this.storagePrefix}:${accountStorageKey}:workspaces`, {\n\t\t\t\t\t\tv: 2,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\tdata: workspaces,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (workspaces != null) {\n\t\t\tthis._workspaces ??= new Map<\n\t\t\t\tstring,\n\t\t\t\tProviderApiCollectionResult<BitbucketWorkspaceDescriptor> | undefined\n\t\t\t>();\n\t\t\tthis._workspaces.set(this._session.accessToken, workspaces);\n\t\t}\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tthis._accounts = undefined;\n\t\tthis._workspaces = undefined;\n\t}\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { Emitter } from "../../../utils/event.js";
2
2
  import { RepositoryDescriptor } from "../../../git/models/resourceDescriptor.js";
3
3
  import { IssueOrPullRequest, IssueOrPullRequestState } from "../../../git/models/issueOrPullRequest.js";
4
4
  import { Issue, IssueSearchCriteria, IssueShape } from "../../../git/models/issue.js";
5
- import { PullRequest, PullRequestFilter, PullRequestMergeMethod, PullRequestSearchCriteria, PullRequestShape, PullRequestStackInfo, PullRequestStateFilter } from "../../../git/models/pullRequest.js";
5
+ import { PullRequest, PullRequestFilter, PullRequestMergeMethod, PullRequestSearchCriteria, PullRequestShape, PullRequestStackInfo, PullRequestStackLayers, PullRequestStateFilter } from "../../../git/models/pullRequest.js";
6
6
  import { Account, UnidentifiedAuthor } from "../../../git/models/author.js";
7
7
  import { DefaultBranch } from "../../../git/models/defaultBranch.js";
8
8
  import { RepositoryMetadata } from "../../../git/models/repositoryMetadata.js";
@@ -246,6 +246,17 @@ declare abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> ex
246
246
  * empty map, which means the repository genuinely has no stacks.
247
247
  */
248
248
  getStacksByPullRequestNumber(owner: string, repo: string, cancellation?: AbortSignal): Promise<Map<number, PullRequestStackInfo> | undefined>;
249
+ /**
250
+ * The ordered layers of the stack the given pull request belongs to, bottom to top.
251
+ *
252
+ * There is no per-pull-request stacks endpoint, so this reuses the same repository-wide stacks read
253
+ * as {@link getStacksByPullRequestNumber} and picks out the one stack containing the given pull
254
+ * request.
255
+ *
256
+ * `undefined` when the host has no stacks concept, the repository isn't enrolled in the preview, or
257
+ * the pull request isn't stacked.
258
+ */
259
+ getStackLayersForPullRequest(owner: string, repo: string, pullRequestNumber: number, cancellation?: AbortSignal): Promise<PullRequestStackLayers | undefined>;
249
260
  protected mergeProviderPullRequest(session: ProviderAuthenticationSession, pr: PullRequest, options?: {
250
261
  mergeMethod?: PullRequestMergeMethod;
251
262
  }, cancellation?: AbortSignal): Promise<boolean>;