@gitkraken/core-gitlens 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -2
- package/dist/git/remotes/azure-devops.js +3 -3
- package/dist/git/remotes/azure-devops.js.map +1 -1
- package/dist/plus/integrations/authentication/configuredIntegrationService.js +9 -2
- package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +1 -1
- package/dist/plus/integrations/collectionMetadata.d.ts +48 -3
- package/dist/plus/integrations/collectionMetadata.js +138 -8
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/errors.d.ts +3 -1
- package/dist/plus/integrations/errors.js +7 -3
- package/dist/plus/integrations/errors.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +3 -3
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +35 -1
- package/dist/plus/integrations/integrationService.js +90 -43
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/lite.js +5 -4
- package/dist/plus/integrations/lite.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +68 -23
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +31 -15
- package/dist/plus/integrations/models/gitHostIntegration.js +71 -22
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +70 -12
- package/dist/plus/integrations/models/integration.js +56 -1
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +17 -0
- package/dist/plus/integrations/models/issuesIntegration.js +2 -0
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/models/pullRequestReads.d.ts +26 -0
- package/dist/plus/integrations/providers/azure/azure.d.ts +66 -1
- package/dist/plus/integrations/providers/azure/azure.js +192 -12
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/search.d.ts +144 -0
- package/dist/plus/integrations/providers/azure/search.js +250 -0
- package/dist/plus/integrations/providers/azure/search.js.map +1 -0
- package/dist/plus/integrations/providers/azureDevOps.d.ts +73 -36
- package/dist/plus/integrations/providers/azureDevOps.js +191 -122
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +176 -0
- package/dist/plus/integrations/providers/azureDevOpsServer.js +560 -0
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.d.ts +12 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.js +128 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +10 -1
- package/dist/plus/integrations/providers/bitbucket-server/models.js +3 -2
- package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +58 -0
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +485 -0
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server.d.ts +61 -3
- package/dist/plus/integrations/providers/bitbucket-server.js +114 -20
- package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket.d.ts +9 -1
- package/dist/plus/integrations/providers/bitbucket.js +13 -2
- package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +6 -6
- package/dist/plus/integrations/providers/github.js +35 -34
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts +6 -4
- package/dist/plus/integrations/providers/gitlab.js +32 -28
- package/dist/plus/integrations/providers/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
- package/dist/plus/integrations/providers/jira-server.d.ts +9 -0
- package/dist/plus/integrations/providers/jira-server.js +15 -7
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +9 -0
- package/dist/plus/integrations/providers/jira.js +14 -2
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/linear.js +2 -2
- package/dist/plus/integrations/providers/models.d.ts +10 -1
- package/dist/plus/integrations/providers/models.js +86 -0
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +10 -0
- package/dist/plus/integrations/providers/providersApi.js +18 -3
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/utils.d.ts +12 -1
- package/dist/plus/integrations/providers/utils.js +36 -6
- package/dist/plus/integrations/providers/utils.js.map +1 -1
- package/dist/plus/integrations/reads/broaden.js +6 -6
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/context.d.ts +1 -1
- package/dist/plus/integrations/reads/counts.d.ts +18 -4
- package/dist/plus/integrations/reads/counts.js +66 -15
- package/dist/plus/integrations/reads/counts.js.map +1 -1
- package/dist/plus/integrations/reads/cursors.js +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +2 -1
- package/dist/plus/integrations/reads/drains.js +1 -1
- package/dist/plus/integrations/reads/filters.d.ts +1 -1
- package/dist/plus/integrations/reads/filters.js +17 -12
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +2 -2
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.utils.js +1 -1
- package/dist/plus/integrations/reads/issueBatch.js +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +2 -2
- package/dist/plus/integrations/reads/issues.js +2 -2
- package/dist/plus/integrations/reads/paging.js +1 -1
- package/dist/plus/integrations/reads/resolveRepository.d.ts +12 -11
- package/dist/plus/integrations/reads/resolveRepository.js +110 -19
- package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
- package/dist/plus/integrations/reads/searchIssues.js +4 -2
- package/dist/plus/integrations/reads/searchIssues.js.map +1 -1
- package/dist/plus/integrations/reads/searchPullRequests.js +9 -3
- package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/sweeps.js +1 -1
- package/dist/plus/integrations/reads/trackerIssue.d.ts +9 -5
- package/dist/plus/integrations/reads/trackerIssue.js +15 -9
- package/dist/plus/integrations/reads/trackerIssue.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +6 -2
- package/dist/plus/integrations/reads/warnings.js +15 -19
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +84 -6
- package/dist/plus/integrations/results.js +9 -2
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/plus/integrations/utils/domain.utils.d.ts +19 -0
- package/dist/plus/integrations/utils/domain.utils.js +36 -1
- package/dist/plus/integrations/utils/domain.utils.js.map +1 -1
- package/dist/plus/integrations/utils/integration.utils.d.ts +1 -0
- package/dist/plus/integrations/utils/integration.utils.js +12 -1
- package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
- package/docs/integrations.md +263 -74
- package/docs/kepler-read-api-parity.md +19 -6
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"azureDevOps.js","names":[],"sources":["../../../../../plus/integrations/src/providers/azureDevOps.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScope, 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\tPullRequestState,\n\tPullRequestStateFilter,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport type { Emitter } from '@gitlens/utils/event.js';\nimport { mapSettledBounded } from '@gitlens/utils/promise.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { IntegrationAuthenticationService } from '../authentication/integrationAuthenticationService.js';\nimport type {\n\tAuthenticationSessionLike as AuthenticationSession,\n\tProviderAuthenticationSession,\n\tTokenWithInfo,\n} from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport type { IntegrationServiceContext } from '../context.js';\nimport type { IntegrationConnectionChangeEvent } from '../integrationService.js';\nimport type { SearchMyPullRequestsOptions, SearchPullRequestsOptions } from '../models/gitHostIntegration.js';\nimport { GitHostIntegration } from '../models/gitHostIntegration.js';\nimport type { AccountWideIssuesResult, IntegrationKey, SearchMyIssuesOptions } from '../models/integration.js';\nimport type {\n\tAzureOrganizationDescriptor,\n\tAzureProjectDescriptor,\n\tAzureProjectInputDescriptor,\n\tAzureRemoteRepositoryDescriptor,\n\tAzureRepositoryDescriptor,\n} from './azure/models.js';\nimport type {\n\tProviderApiCollectionResult,\n\tProviderApiPagedResult,\n\tProviderHierarchyResult,\n\tProviderOrganization,\n\tProviderPullRequest,\n\tProviderRepoInput,\n\tProviderRepository,\n} from './models.js';\nimport {\n\tfromProviderIssue,\n\tfromProviderPullRequest,\n\tgetProviderPullRequestIdentity,\n\tIssueFilter,\n\tproviderPullRequestMatchesSearch,\n\tprovidersMetadata,\n\tPullRequestFilter,\n\ttoProviderPullRequestStates,\n} from './models.js';\nimport type { ProvidersApi } from './providersApi.js';\nimport {\n\tcollectProviderPagedResult,\n\tflatSettledResultsOrThrow,\n\tmergeCollectionMetadata,\n} from './utils/providerPaging.js';\n\nfunction getAzureRepositoryIdentity(repo: Pick<AzureRepositoryDescriptor, 'owner' | 'name' | 'project'>): {\n\tresourceName: string;\n\tprojectName?: string;\n\trepositoryName: string;\n} {\n\tconst match = /^([^/]+)\\/_git\\/([^/]+)$/i.exec(repo.name);\n\treturn {\n\t\tresourceName: repo.owner,\n\t\tprojectName: repo.project ?? match?.[1],\n\t\trepositoryName: match?.[2] ?? repo.name,\n\t};\n}\n\nfunction getAzureRepositoryApiBaseUrl(baseUrl: string, repo: Pick<AzureRepositoryDescriptor, 'virtualDirectory'>) {\n\tif (repo.virtualDirectory == null) return baseUrl;\n\n\tconst segments = repo.virtualDirectory.split('/');\n\tif (segments.some(segment => !segment || segment === '.' || segment === '..')) {\n\t\tthrow new Error(`Invalid Azure virtual directory '${repo.virtualDirectory}'.`);\n\t}\n\n\treturn `${baseUrl.replace(/\\/+$/, '')}/${segments.map(encodeURIComponent).join('/')}`;\n}\n\n/**\n * Matches an org/project descriptor against a caller-supplied name, mirroring the facade's own\n * key/id/name comparison so `listIssuesPage({ org, project })` narrows on the same identifiers a consumer\n * already got back from `listOrgs`/`listProjects`.\n */\nfunction azureResourceMatches(resource: { key?: string; id?: string; name?: string }, value: string): boolean {\n\treturn resource.key === value || resource.id === value || resource.name === value;\n}\n\nexport abstract class AzureDevOpsIntegrationBase<\n\tTIntegrationId extends GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer,\n\tTRepositoryDescriptor extends AzureRepositoryDescriptor = AzureRepositoryDescriptor,\n> extends GitHostIntegration<TIntegrationId, TRepositoryDescriptor> {\n\tprotected abstract get apiBaseUrl(): string;\n\tprotected getApiOptions(\n\t\tsession: ProviderAuthenticationSession,\n\t\tdoNotConvertToPat: boolean = false,\n\t): {\n\t\ttokenWithInfo: TokenWithInfo<TIntegrationId>;\n\t\toptions: { isPAT: boolean; baseUrl?: string };\n\t} {\n\t\t// The secret is handed over raw: `isPAT` tells provider-apis to send it as an HTTP Basic credential, which\n\t\t// it encodes itself as `base64(':' + token)`. Pre-encoding here would be encoded a second time and refused\n\t\t// by Azure DevOps. `getCurrentUser` is the one read that needs a bearer token, and passes\n\t\t// `doNotConvertToPat` to get one.\n\t\tconst usePat = !doNotConvertToPat;\n\t\tconst tokenWithInfo = toTokenWithInfo<TIntegrationId>(this.id, session);\n\t\treturn {\n\t\t\ttokenWithInfo: tokenWithInfo,\n\t\t\toptions: { isPAT: usePat },\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 user = await this._requestForCurrentUser(session);\n\t\t\tthis._accounts.set(accessToken, user);\n\t\t}\n\n\t\treturn this._accounts.get(accessToken);\n\t}\n\n\tprotected async _requestForCurrentUser(session: ProviderAuthenticationSession): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo, options } = this.getApiOptions(session, true);\n\t\tconst user = await api.getCurrentUser(tokenWithInfo, options);\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\tprivate _organizations: Map<string, AzureOrganizationDescriptor[] | undefined> | undefined;\n\tprivate async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<AzureOrganizationDescriptor[] | undefined> {\n\t\tthis._organizations ??= new Map<string, AzureOrganizationDescriptor[] | undefined>();\n\t\tconst { accessToken } = session;\n\t\tconst cachedResources = this._organizations.get(accessToken);\n\n\t\tif (cachedResources == null || force) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst account = await this.getProviderCurrentAccount(session);\n\t\t\tif (account?.id == null) return undefined;\n\n\t\t\tconst { tokenWithInfo, options } = this.getApiOptions(session);\n\t\t\tconst resources = await api.getAzureResourcesForUser(tokenWithInfo, account.id, options);\n\t\t\tthis._organizations.set(\n\t\t\t\taccessToken,\n\t\t\t\tresources != null ? resources.map(r => ({ ...r, key: r.id })) : undefined,\n\t\t\t);\n\t\t}\n\n\t\treturn this._organizations.get(accessToken);\n\t}\n\n\tprivate _projects: Map<string, AzureProjectDescriptor[] | undefined> | undefined;\n\t/**\n\t * Discovers (and caches) each resource's projects. Only resources whose drain completed cleanly are cached;\n\t * a rejected or backstop-truncated drain is left uncached (retried next call). When `failures` is supplied,\n\t * a rejected resource is recorded there as a structured {@link CollectionScopeFailure} so an account-wide\n\t * caller can surface the incomplete project set (a whole org's PRs/issues silently missing otherwise) as a\n\t * scope-aware warning + `fetchFailed` rather than an all-pages success over a hole.\n\t */\n\tprivate async getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: AzureOrganizationDescriptor[],\n\t\tforce: boolean = false,\n\t\tfailures?: CollectionScopeFailure[],\n\t): Promise<ProviderApiCollectionResult<AzureProjectDescriptor>> {\n\t\tthis._projects ??= new Map<string, AzureProjectDescriptor[] | undefined>();\n\t\tconst { accessToken } = session;\n\n\t\tlet resourcesWithoutProjects = [];\n\t\tif (force) {\n\t\t\tresourcesWithoutProjects = resources;\n\t\t} else {\n\t\t\tfor (const resource of resources) {\n\t\t\t\tconst resourceKey = `${accessToken}:${resource.id}`;\n\t\t\t\tconst cachedProjects = this._projects.get(resourceKey);\n\t\t\t\tif (cachedProjects == null) {\n\t\t\t\t\tresourcesWithoutProjects.push(resource);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst allProjects: AzureProjectDescriptor[] = [];\n\t\tlet resultMetadata: CollectionMetadata | undefined;\n\n\t\tif (resourcesWithoutProjects.length > 0) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst { tokenWithInfo, options } = this.getApiOptions(session);\n\t\t\t// The projects API is paginated; a single call would drop every project past the first page (and\n\t\t\t// with it their repos and PRs). Drain all pages per resource, threading the returned cursor.\n\t\t\t// Per-resource (not a shared flatSettled) so a resource whose drain was truncated (hit the paging\n\t\t\t// backstop) or rejected is NOT cached — caching a partial list here would make every later repo/PR/\n\t\t\t// issue read for that org silently inherit an incomplete project set. Leaving it uncached means the\n\t\t\t// next call retries it. The scope is passed so a page-level failure preserves the prefix already\n\t\t\t// fetched and records a structured failure instead of re-throwing.\n\t\t\tconst drains = await Promise.allSettled(\n\t\t\t\tresourcesWithoutProjects.map(async resource => ({\n\t\t\t\t\tresource: resource,\n\t\t\t\t\tresult: await collectProviderPagedResult(\n\t\t\t\t\t\tcursor =>\n\t\t\t\t\t\t\tapi.getAzureProjectsForResource(tokenWithInfo, resource.name, {\n\t\t\t\t\t\t\t\t...options,\n\t\t\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t20,\n\t\t\t\t\t\t{ providerId: this.id, resourceId: resource.id },\n\t\t\t\t\t),\n\t\t\t\t})),\n\t\t\t);\n\n\t\t\t// `allSettled` preserves order, so `drains[i]` is `resourcesWithoutProjects[i]`.\n\t\t\tdrains.forEach((drain, i) => {\n\t\t\t\t// A rejected resource drain contributes nothing and is left uncached (retried next call). Record\n\t\t\t\t// it as a structured failure so an account-wide caller can warn on the org whose projects (and\n\t\t\t\t// thus PRs/issues) are missing, instead of silently narrowing the read.\n\t\t\t\tif (drain.status !== 'fulfilled') {\n\t\t\t\t\tconst resource = resourcesWithoutProjects[i];\n\t\t\t\t\tconst failure = toCollectionScopeFailure(\n\t\t\t\t\t\t{ providerId: this.id, resourceId: resource.id },\n\t\t\t\t\t\tdrain.reason,\n\t\t\t\t\t);\n\t\t\t\t\tfailures?.push(failure);\n\t\t\t\t\tresultMetadata = mergeCollectionMetadata(resultMetadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [failure],\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst { resource, result } = drain.value;\n\t\t\t\tconst projects = result.values\n\t\t\t\t\t.filter(p => p.namespace === resource.name)\n\t\t\t\t\t.map(p => ({\n\t\t\t\t\t\tid: p.id,\n\t\t\t\t\t\tname: p.name,\n\t\t\t\t\t\tresourceId: resource.id,\n\t\t\t\t\t\tresourceName: resource.name,\n\t\t\t\t\t\tkey: p.id,\n\t\t\t\t\t}));\n\n\t\t\t\tif (result.metadata != null) {\n\t\t\t\t\tresultMetadata = mergeCollectionMetadata(resultMetadata, result.metadata);\n\t\t\t\t}\n\n\t\t\t\tconst metadataIncomplete = result.metadata != null && result.metadata.completeness !== 'complete';\n\t\t\t\tif (result.truncated || metadataIncomplete) {\n\t\t\t\t\t// A truncated drain is an incomplete project set; include its partial values in the current\n\t\t\t\t\t// result but don't cache it as if complete. Add a structured failure for the truncation unless\n\t\t\t\t\t// the drain already recorded a page-level failure for this resource.\n\t\t\t\t\tif (\n\t\t\t\t\t\tresult.truncated &&\n\t\t\t\t\t\t!result.metadata?.failures?.some(\n\t\t\t\t\t\t\tf => f.scope?.resourceId === resource.id && f.kind !== 'unknown',\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst failure = toCollectionScopeFailure(\n\t\t\t\t\t\t\t{ providerId: this.id, resourceId: resource.id },\n\t\t\t\t\t\t\tnew Error('Project discovery was truncated before all pages were read'),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tfailures?.push(failure);\n\t\t\t\t\t\tresultMetadata = mergeCollectionMetadata(resultMetadata, {\n\t\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\t\tfailures: [failure],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tallProjects.push(...projects);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._projects!.set(`${accessToken}:${resource.id}`, projects);\n\t\t\t});\n\t\t}\n\n\t\tconst cachedProjects = resources.reduce<AzureProjectDescriptor[]>((projects, resource) => {\n\t\t\tconst resourceProjects = this._projects!.get(`${accessToken}:${resource.id}`);\n\t\t\tif (resourceProjects != null) {\n\t\t\t\tprojects.push(...resourceProjects);\n\t\t\t}\n\t\t\treturn projects;\n\t\t}, []);\n\t\tallProjects.push(...cachedProjects);\n\n\t\tconst projectsByIdentity = new Map<string, AzureProjectDescriptor>();\n\t\tfor (const project of allProjects) {\n\t\t\tconst identity = `${project.resourceId}:${project.id}`;\n\t\t\tif (!projectsByIdentity.has(identity)) {\n\t\t\t\tprojectsByIdentity.set(identity, project);\n\t\t\t}\n\t\t}\n\t\tconst values = [...projectsByIdentity.values()];\n\t\treturn resultMetadata != null ? { values: values, metadata: resultMetadata } : { values: values };\n\t}\n\n\tprivate async getRepoDescriptorsForProjects(\n\t\tsession: ProviderAuthenticationSession,\n\t\tprojects: AzureProjectDescriptor[],\n\t): Promise<Map<string, AzureRemoteRepositoryDescriptor[] | undefined>> {\n\t\tconst descriptors = new Map<string, AzureRemoteRepositoryDescriptor[] | undefined>();\n\t\tif (projects.length === 0) return descriptors;\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo, options } = this.getApiOptions(session);\n\t\tawait Promise.all(\n\t\t\tprojects.map(async project => {\n\t\t\t\tconst repos = (\n\t\t\t\t\tawait api.getReposForAzureProject(tokenWithInfo, project.resourceName, project.name, options)\n\t\t\t\t)?.values;\n\t\t\t\tif (repos != null && repos.length > 0) {\n\t\t\t\t\tdescriptors.set(\n\t\t\t\t\t\tproject.id,\n\t\t\t\t\t\trepos.map(r => ({\n\t\t\t\t\t\t\tid: r.id,\n\t\t\t\t\t\t\tnodeId: r.graphQLId ?? undefined,\n\t\t\t\t\t\t\tresourceName: project.resourceName,\n\t\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\t\tprojectName: project.name,\n\t\t\t\t\t\t\turl: r.webUrl ?? undefined,\n\t\t\t\t\t\t\tcloneUrlHttps: r.httpsUrl ?? undefined,\n\t\t\t\t\t\t\tcloneUrlSsh: r.sshUrl ?? undefined,\n\t\t\t\t\t\t\tkey: r.id,\n\t\t\t\t\t\t})),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\treturn descriptors;\n\t}\n\n\tprotected override async getProviderOrganizationsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null) return undefined;\n\n\t\treturn {\n\t\t\tvalues: orgs.map(o => ({\n\t\t\t\tid: o.id,\n\t\t\t\tproviderId: this.id,\n\t\t\t\tname: o.name,\n\t\t\t\turl: `${this.apiBaseUrl}/${o.name}`,\n\t\t\t})),\n\t\t};\n\t}\n\n\tprotected override async getProviderProjectsForOrg(\n\t\tsession: ProviderAuthenticationSession,\n\t\torg?: string,\n\t): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {\n\t\t// Azure is the one git host with a project tier: repos live under org (resource) → project. Enumerate\n\t\t// the user's orgs (optionally scoped to `org`), read their projects, and surface each as an org-shaped\n\t\t// entry so the ProviderBackend facade can list them uniformly.\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null) return undefined;\n\t\tif (orgs.length === 0) {\n\t\t\treturn { values: [], paging: { cursor: '{}', more: false } };\n\t\t}\n\n\t\tconst scopedOrgs = org != null ? orgs.filter(o => o.name === org || o.id === org) : orgs;\n\t\tif (scopedOrgs.length === 0) return { values: [] };\n\n\t\tconst projects = await this.getProviderProjectsForResources(session, scopedOrgs);\n\t\tif (projects.values.length === 0 && projects.metadata == null) return { values: [] };\n\n\t\treturn {\n\t\t\tvalues: projects.values.map(p => ({\n\t\t\t\tid: p.id,\n\t\t\t\tproviderId: this.id,\n\t\t\t\tname: p.name,\n\t\t\t\torg: p.resourceName,\n\t\t\t\turl: `${this.apiBaseUrl}/${p.resourceName}/${p.name}`,\n\t\t\t})),\n\t\t\t...(projects.metadata != null ? { metadata: projects.metadata } : {}),\n\t\t};\n\t}\n\n\t/**\n\t * With `options.project`, returns one page of that project's repos (follow `paging.cursor` to page).\n\t * Without a project it fans out across every project under `org` and returns them all at once — there's\n\t * no single cursor to page a parallel merge — skipping any project that fails to list rather than\n\t * failing the whole org. If any successful project drain hits the defensive page backstop, the merged\n\t * result is marked `truncated` without exposing a synthetic cursor.\n\t */\n\tprotected override async getProviderRepositoriesForOrg(\n\t\tsession: ProviderAuthenticationSession,\n\t\torg: string,\n\t\toptions?: { project?: string; cursor?: string },\n\t): Promise<ProviderHierarchyResult<ProviderRepository> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);\n\n\t\tif (options?.project) {\n\t\t\treturn api.getReposForAzureProject(tokenWithInfo, org, options.project, {\n\t\t\t\t...apiOptions,\n\t\t\t\tcursor: options.cursor,\n\t\t\t});\n\t\t}\n\n\t\tconst orgDescriptor = (await this.getProviderResourcesForUser(session))?.find(o => o.name === org);\n\t\tif (orgDescriptor == null) return undefined;\n\n\t\tconst discoveryFailures: CollectionScopeFailure[] = [];\n\t\tconst projects = await this.getProviderProjectsForResources(session, [orgDescriptor], false, discoveryFailures);\n\t\t// An empty result is only proven-empty when discovery itself succeeded; a rejected project-discovery\n\t\t// leaves the repo set unknowable, so surface the discovery metadata rather than publishing a hole as a\n\t\t// complete list.\n\t\tif (projects.values.length === 0) {\n\t\t\treturn { values: [], metadata: projects.metadata };\n\t\t}\n\n\t\tlet repoMetadata: CollectionMetadata | undefined;\n\t\tconst results = await Promise.allSettled(\n\t\t\tprojects.values.map(p =>\n\t\t\t\tcollectProviderPagedResult(\n\t\t\t\t\tcursor =>\n\t\t\t\t\t\tapi.getReposForAzureProject(tokenWithInfo, org, p.name, { ...apiOptions, cursor: cursor }),\n\t\t\t\t\t20,\n\t\t\t\t\t{ providerId: this.id, resourceId: org, projectId: p.name },\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\n\t\tconst values: ProviderRepository[] = [];\n\t\tlet truncated = false;\n\t\tfor (const result of results) {\n\t\t\t// With a per-project scope, collectProviderPagedResult catches page-level failures itself and returns\n\t\t\t// them as metadata rather than rejecting. A rejected promise here is an unexpected internal error.\n\t\t\tif (result.status !== 'fulfilled') {\n\t\t\t\ttruncated = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvalues.push(...result.value.values);\n\t\t\tif (result.value.metadata != null) {\n\t\t\t\trepoMetadata = mergeCollectionMetadata(repoMetadata, result.value.metadata);\n\t\t\t}\n\t\t\ttruncated ||= result.value.truncated === true;\n\t\t}\n\n\t\tconst metadata = mergeCollectionMetadata(repoMetadata, projects.metadata);\n\t\treturn {\n\t\t\tvalues: values,\n\t\t\t...(metadata != null ? { metadata: metadata } : {}),\n\t\t\t...(truncated || (metadata != null && metadata.completeness !== 'complete') ? { truncated: true } : {}),\n\t\t};\n\t}\n\n\tprotected override async mergeProviderPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tpr: PullRequest,\n\t\toptions?: {\n\t\t\tmergeMethod?: PullRequestMergeMethod;\n\t\t},\n\t): Promise<boolean> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif (pr.refs == null || pr.project == null) return false;\n\n\t\tconst { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);\n\n\t\ttry {\n\t\t\tconst merged = await api.mergePullRequest(tokenWithInfo, pr, {\n\t\t\t\t...options,\n\t\t\t\t...apiOptions,\n\t\t\t});\n\t\t\treturn merged;\n\t\t} catch (ex) {\n\t\t\tthis.showMergeErrorMessage(ex);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected showMergeErrorMessage(ex: Error): void {\n\t\tthis.ctx.hooks?.ui?.onError?.(\n\t\t\t`${ex.message}. Check branch policies, and ensure you have the necessary permissions to merge the pull request.`,\n\t\t);\n\t}\n\n\tprotected override async getProviderAccountForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<UnidentifiedAuthor | undefined> {\n\t\treturn (await this.authenticationService.apis.azure)?.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\tgetAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo),\n\t\t\toptions,\n\t\t);\n\t}\n\n\tprotected override async getProviderAccountForEmail(\n\t\t_session: AuthenticationSession,\n\t\t_repo: AzureRepositoryDescriptor,\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: AzureRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<DefaultBranch | undefined> {\n\t\treturn (await this.authenticationService.apis.azure)?.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: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo) },\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\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.azure)?.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\t{\n\t\t\t\tbaseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo),\n\t\t\t\ttype: type,\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: AzureProjectInputDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\tif (user?.username == null) return undefined;\n\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null || orgs.length === 0) return undefined;\n\n\t\tconst projects = await this.getProviderProjectsForResources(session, orgs);\n\t\tif (projects.values.length === 0) return undefined;\n\n\t\tconst matchingProject = projects.values.find(p => p.resourceName === project.owner && p.name === project.name);\n\t\tif (matchingProject == null) return undefined;\n\n\t\treturn (await this.authenticationService.apis.azure)?.getIssue(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tmatchingProject,\n\t\t\tid,\n\t\t\t{\n\t\t\t\tbaseUrl: this.apiBaseUrl,\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\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.azure)?.getPullRequestForBranch(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tbranch,\n\t\t\t{\n\t\t\t\tbaseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\n\t\trev: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.azure)?.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\tgetAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo),\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\tvirtualDirectory?: string;\n\t\tconnectionId?: string;\n\t}): Promise<ProviderRepository | undefined> {\n\t\tconst identity = getAzureRepositoryIdentity(repo);\n\t\tif (identity.projectName == null) return undefined;\n\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.resolveReadSession(repo.connectionId, undefined);\n\t\tif (session == null) return undefined;\n\n\t\tconst { tokenWithInfo, options } = this.getApiOptions(session);\n\t\treturn api.getRepo(tokenWithInfo, identity.resourceName, identity.repositoryName, identity.projectName, {\n\t\t\t...options,\n\t\t\tbaseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo),\n\t\t});\n\t}\n\n\tprotected override async getProviderRepositoryMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\treturn (await this.authenticationService.apis.azure)?.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: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo) },\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async searchProviderMyPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: AzureRepositoryDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t\toptions?: SearchMyPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\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 states = toProviderPullRequestStates(options?.state);\n\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\t// Azure filters key on the identity GUID (account id), not the display name — see\n\t\t// getProviderMyPullRequestsForUser and the repo-scoped path in gitHostIntegration.ts.\n\t\tif (user?.id == null) return undefined;\n\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null || orgs.length === 0) return undefined;\n\n\t\tconst projects = await this.getProviderProjectsForResources(session, orgs);\n\t\tif (projects.values.length === 0) return undefined;\n\n\t\tconst repoDescriptors = [\n\t\t\t...((await this.getRepoDescriptorsForProjects(session, projects.values)) ?? new Map()).values(),\n\t\t]\n\t\t\t.filter(r => r != null)\n\t\t\t.flat();\n\n\t\tconst { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);\n\t\tconst projectInputs = projects.values.map(p => ({ namespace: p.resourceName, project: p.name }));\n\t\t// Legacy array-returning path (Launchpad/focus view): unwrap `.values` from the SDK collection result.\n\t\t// The metadata (partial/failures) isn't surfaced here because this path's return type has no warning\n\t\t// channel; the metadata-aware ProviderBackend surface is getProviderMyPullRequestsForUser above.\n\t\tconst assignedPrs = (\n\t\t\tawait api.getPullRequestsForAzureProjects(tokenWithInfo, projectInputs, {\n\t\t\t\t...apiOptions,\n\t\t\t\tassigneeLogins: [user.id],\n\t\t\t\tstates: states,\n\t\t\t})\n\t\t).values.map(pr => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects.values));\n\t\tconst authoredPrs = (\n\t\t\tawait api.getPullRequestsForAzureProjects(tokenWithInfo, projectInputs, {\n\t\t\t\t...apiOptions,\n\t\t\t\tauthorLogin: user.id,\n\t\t\t\tstates: states,\n\t\t\t})\n\t\t).values.map(pr => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects.values));\n\t\tconst prsById = new Map<string, PullRequest>();\n\t\tfor (const pr of authoredPrs) {\n\t\t\tprsById.set(pr.id, pr);\n\t\t}\n\n\t\tfor (const pr of assignedPrs) {\n\t\t\tconst existing = prsById.get(pr.id);\n\t\t\tif (existing == null) {\n\t\t\t\tprsById.set(pr.id, pr);\n\t\t\t}\n\t\t}\n\n\t\treturn [...prsById.values()];\n\t}\n\n\tprotected override async getProviderMyPullRequestsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: { state?: PullRequestStateFilter[]; cursor?: string; filters?: PullRequestFilter[] },\n\t): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\t// Azure routes authorLogin/assigneeLogins to `searchCriteria.creatorId`/`reviewerId`, which require the\n\t\t// identity GUID (account id), not the display name — matching the repo-scoped path in\n\t\t// gitHostIntegration.ts. Using `username` here would match nothing and return zero PRs.\n\t\tif (user?.id == null) return undefined;\n\n\t\t// Azure PRs are org + project scoped: enumerate the user's orgs and their projects, then read authored\n\t\t// and assigned PRs across all of them. Return the raw provider shape (not the normalized model) so the\n\t\t// ProviderBackend surface stays uniform with the other providers.\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null || orgs.length === 0) return undefined;\n\n\t\t// Structured per-scope failures from BOTH project discovery (a whole org dropped) and the per-project PR\n\t\t// drains, so the facade warns on the failed scope + sets `fetchFailed` instead of silently narrowing.\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tconst projects = await this.getProviderProjectsForResources(session, orgs, false, failures);\n\t\tif (projects.values.length === 0) {\n\t\t\t// Project discovery itself was incomplete (e.g. a truncated org); surface the metadata so the facade\n\t\t\t// can warn and set fetchFailed rather than reporting an empty account.\n\t\t\tconst incomplete = projects.metadata != null && projects.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...(projects.metadata != null ? { metadata: projects.metadata } : {}),\n\t\t\t};\n\t\t}\n\n\t\tconst { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);\n\t\tconst states = toProviderPullRequestStates(options?.state);\n\t\tconst maxPagesPerProject = 20;\n\n\t\t// Drain each project fully (numbered pages) for both the authored and assigned reads. Azure has no\n\t\t// single cross-project cursor, so the aggregate is one page; `truncated` is set only if a project hit\n\t\t// the backstop with more pages remaining.\n\t\tlet truncated = projects.metadata != null && projects.metadata.completeness !== 'complete';\n\t\t// Drain one project's numbered pages, returning its PRs and any per-page failure. Returns per-project\n\t\t// (not mutating shared state) so the fan-out below can be settled independently: one project's read\n\t\t// failure must not discard every other project's already-drained PRs.\n\t\tconst drainProject = async (\n\t\t\tproject: { namespace: string; project: string },\n\t\t\tscope: CollectionScope,\n\t\t\tfilter: { authorLogin?: string; assigneeLogins?: string[]; reviewerId?: string },\n\t\t): Promise<{ prs: ProviderPullRequest[]; projectIdentity: string; failure?: CollectionScopeFailure }> => {\n\t\t\tconst collected: ProviderPullRequest[] = [];\n\t\t\tconst projectIdentity = `${project.namespace}/${project.project}`;\n\t\t\tlet page: number | undefined;\n\t\t\tfor (let i = 0; i < maxPagesPerProject; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await api.getPullRequestsForAzureProject(tokenWithInfo, project, {\n\t\t\t\t\t\t...apiOptions,\n\t\t\t\t\t\t...filter,\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t\tpage: page,\n\t\t\t\t\t});\n\t\t\t\t\tif (result == null) {\n\t\t\t\t\t\tif (page == null) break;\n\n\t\t\t\t\t\ttruncated = true;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tprs: collected,\n\t\t\t\t\t\t\tprojectIdentity: projectIdentity,\n\t\t\t\t\t\t\tfailure: toCollectionScopeFailure(\n\t\t\t\t\t\t\t\tscope,\n\t\t\t\t\t\t\t\tnew Error('Azure DevOps returned no page after advertising a continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tcollected.push(...result.data);\n\t\t\t\t\tif (!result.hasMore) break;\n\t\t\t\t\tif (result.nextPage == null || result.nextPage === page) {\n\t\t\t\t\t\ttruncated = true;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tprs: collected,\n\t\t\t\t\t\t\tprojectIdentity: projectIdentity,\n\t\t\t\t\t\t\tfailure: toCollectionScopeFailure(\n\t\t\t\t\t\t\t\tscope,\n\t\t\t\t\t\t\t\tnew Error('Azure DevOps returned no advancing pull request continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tpage = result.nextPage;\n\t\t\t\t\tif (i === maxPagesPerProject - 1) {\n\t\t\t\t\t\ttruncated = true;\n\t\t\t\t\t}\n\t\t\t\t} catch (ex) {\n\t\t\t\t\t// A page failure after the first page leaves the already-drained prefix intact; record the\n\t\t\t\t\t// failure at the project scope instead of re-throwing and discarding the prefix.\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tprs: collected,\n\t\t\t\t\t\tprojectIdentity: projectIdentity,\n\t\t\t\t\t\tfailure: toCollectionScopeFailure(scope, ex),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { prs: collected, projectIdentity: projectIdentity };\n\t\t};\n\n\t\t// Settle per-project failures instead of rejecting the whole sweep. `drainProject` already catches its own\n\t\t// page-level failures, so the structured failure (attributed to that project) is preserved rather than\n\t\t// re-thrown — re-throwing an auth/rate-limit rejection would discard every other project's already-drained\n\t\t// PRs. Auth/rate-limit stay actionable through the failure's kind (the facade maps it to an `auth`/`rate-limit`\n\t\t// warning + `fetchFailed`), matching the SDK's model. `failures` was declared above so project-discovery\n\t\t// failures and per-project drain failures share it.\n\t\tconst requested = options?.filters?.length ? new Set(options.filters) : undefined;\n\t\tconst wantAuthored = requested == null || requested.has(PullRequestFilter.Author);\n\t\t// Azure has no assignee concept distinct from reviewer. Both neutral relationships map to reviewerId.\n\t\tconst wantReviewed =\n\t\t\trequested == null ||\n\t\t\trequested.has(PullRequestFilter.Assignee) ||\n\t\t\trequested.has(PullRequestFilter.ReviewRequested);\n\t\tconst outcomes = await Promise.all(\n\t\t\tprojects.values.flatMap(p => {\n\t\t\t\tconst project = { namespace: p.resourceName, project: p.name };\n\t\t\t\tconst scope = { providerId: this.id, resourceId: p.resourceId, projectId: p.name };\n\t\t\t\tconst drains = [];\n\t\t\t\tif (wantAuthored) {\n\t\t\t\t\tdrains.push(drainProject(project, scope, { authorLogin: user.id }));\n\t\t\t\t}\n\t\t\t\tif (wantReviewed) {\n\t\t\t\t\tdrains.push(drainProject(project, scope, { reviewerId: user.id }));\n\t\t\t\t}\n\t\t\t\treturn drains;\n\t\t\t}),\n\t\t);\n\n\t\t// Azure's `pullRequestId` is not account-global. Prefer repository + PR id, then the org-qualified URL.\n\t\t// If neither is available, preserve the row instead of collapsing unrelated repositories by numeric id.\n\t\tconst prsByIdentity = new Map<string, ProviderPullRequest>();\n\t\tfor (const outcome of outcomes) {\n\t\t\tif (outcome.failure != null) {\n\t\t\t\tfailures.push(outcome.failure);\n\t\t\t}\n\t\t\tfor (const pr of outcome.prs) {\n\t\t\t\tconst key =\n\t\t\t\t\tgetProviderPullRequestIdentity(pr) ?? `project:${outcome.projectIdentity}:pull-request:${pr.id}`;\n\t\t\t\tif (!prsByIdentity.has(key)) {\n\t\t\t\t\tprsByIdentity.set(key, pr);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst metadata: CollectionMetadata | undefined = mergeCollectionMetadata(\n\t\t\tfailures.length > 0 ? { completeness: 'partial', failures: failures } : undefined,\n\t\t\tprojects.metadata,\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\tprotected override async searchProviderPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\tsearchQuery: string,\n\t\trepos?: AzureRepositoryDescriptor[],\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 orgs = await this.getProviderResourcesForUser(session);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\tif (orgs == null || orgs.length === 0) return undefined;\n\n\t\t// `getProviderProjectsForResources` returns a collection result ({ values, metadata }); this search\n\t\t// path only needs the resolved projects, so read `.values`.\n\t\tconst projects = (await this.getProviderProjectsForResources(session, orgs)).values;\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\tif (projects.length === 0) return undefined;\n\n\t\tconst repoDescriptorsByProject = await this.getRepoDescriptorsForProjects(session, projects);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst repoDescriptors = [...repoDescriptorsByProject.values()].filter(r => r != null).flat();\n\t\tconst requestedRepos = repos?.map(getAzureRepositoryIdentity);\n\t\tconst repoInputs =\n\t\t\trequestedRepos == null\n\t\t\t\t? undefined\n\t\t\t\t: repoDescriptors.filter(r =>\n\t\t\t\t\t\trequestedRepos.some(\n\t\t\t\t\t\t\trepo =>\n\t\t\t\t\t\t\t\trepo.resourceName === r.resourceName &&\n\t\t\t\t\t\t\t\trepo.repositoryName === r.name &&\n\t\t\t\t\t\t\t\t(repo.projectName == null || repo.projectName === r.projectName),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\tif (repoInputs?.length === 0) return [];\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);\n\t\tconst states = toProviderPullRequestStates(options?.include);\n\t\tconst searchScopes: { project: { namespace: string; project: string }; repo?: ProviderRepoInput }[] =\n\t\t\trepoInputs != null\n\t\t\t\t? repoInputs.flatMap(repo =>\n\t\t\t\t\t\trepo.projectName == null\n\t\t\t\t\t\t\t? []\n\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\t\tproject: { namespace: repo.resourceName, project: repo.projectName },\n\t\t\t\t\t\t\t\t\t\trepo: {\n\t\t\t\t\t\t\t\t\t\t\tid: repo.id,\n\t\t\t\t\t\t\t\t\t\t\tname: repo.name,\n\t\t\t\t\t\t\t\t\t\t\tnamespace: repo.resourceName,\n\t\t\t\t\t\t\t\t\t\t\tproject: repo.projectName,\n\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)\n\t\t\t\t: projects.map(project => ({\n\t\t\t\t\t\tproject: { namespace: project.resourceName, project: project.name },\n\t\t\t\t\t}));\n\n\t\tconst providerPullRequests = flatSettledResultsOrThrow(\n\t\t\tawait mapSettledBounded(searchScopes, providerFanOutConcurrency, async scope => {\n\t\t\t\tconst values: ProviderPullRequest[] = [];\n\t\t\t\tlet page: number | undefined;\n\t\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\tconst result = await api.getPullRequestsForAzureProject(tokenWithInfo, scope.project, {\n\t\t\t\t\t\t...apiOptions,\n\t\t\t\t\t\tpage: page,\n\t\t\t\t\t\trepo: scope.repo,\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t});\n\t\t\t\t\tif (result == null) break;\n\n\t\t\t\t\tvalues.push(...result.data);\n\t\t\t\t\tif (!result.hasMore || result.nextPage == null) break;\n\n\t\t\t\t\tpage = result.nextPage;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}),\n\t\t);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\treturn [...new Map(providerPullRequests.map(pr => [pr.url ?? `${pr.repository.id}:${pr.id}`, pr])).values()]\n\t\t\t.filter(pr => providerPullRequestMatchesSearch(pr, searchQuery))\n\t\t\t.map(pr => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects));\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: AzureRepositoryDescriptor[],\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, repos))?.values;\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for Azure = the user's authored + assigned work items across every project of\n\t * every org. Azure's issue read is numbered-page, so each (project × filter) read is drained to exhaustion\n\t * (bounded by a defensive per-read backstop). Unlike a silent `flatSettled`, a project read that was\n\t * truncated by the backstop or rejected outright is recorded as `truncated`, so the facade reports an\n\t * incomplete read instead of publishing a partial list as complete.\n\t *\n\t * `searchOptions.org`/`.project` narrow the fan-out server-side (each drain is already a per-project read,\n\t * so scoping just selects which projects to drain). Without this there was no way to ask for \"work items in\n\t * project P\": a consumer had to filter the account-wide page client-side, which desynchronizes the filtered\n\t * `items` from the `hasMore`/`currentPage` of the pre-filter read — a project-less page reads as \"no issues\"\n\t * while `hasMore` is still true.\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources?: ResourceDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t\tsearchOptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\tif (user?.username == null) return undefined;\n\n\t\tconst allOrgs = await this.getProviderResourcesForUser(session);\n\t\tif (allOrgs == null) return undefined;\n\t\tif (allOrgs.length === 0) return { values: [], truncated: false };\n\n\t\t// Scope by org first so project discovery only fans out over the requested account. An org filter that\n\t\t// matches nothing is an empty-but-successful read, not an unsupported one: returning `undefined` here\n\t\t// would be reported as \"account-wide issue search is not supported by this provider\".\n\t\tconst orgs =\n\t\t\tsearchOptions?.org != null ? allOrgs.filter(o => azureResourceMatches(o, searchOptions.org!)) : allOrgs;\n\t\tif (orgs.length === 0) return { values: [], truncated: false };\n\n\t\t// Structured per-scope failures from BOTH project discovery (a whole org dropped) and the per-project\n\t\t// issue drains, so the facade warns on the failed scope + sets `fetchFailed` instead of narrowing silently.\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tconst discovered = await this.getProviderProjectsForResources(session, orgs, false, failures);\n\t\tconst projects =\n\t\t\tsearchOptions?.project != null\n\t\t\t\t? {\n\t\t\t\t\t\t...discovered,\n\t\t\t\t\t\tvalues: discovered.values.filter(p => azureResourceMatches(p, searchOptions.project!)),\n\t\t\t\t\t}\n\t\t\t\t: discovered;\n\t\tif (projects.values.length === 0) {\n\t\t\t// An explicitly-scoped read that found no matching project is an empty SUCCESS: `undefined` here is\n\t\t\t// reported as \"account-wide issue search is not supported by this provider\", which would send the\n\t\t\t// consumer down a repo-scoped fallback for what is simply an empty scope. Only an incomplete discovery\n\t\t\t// (which may itself have dropped the requested project) is truncated, and its metadata is forwarded so\n\t\t\t// the facade still warns on the failed scope.\n\t\t\treturn projects.metadata != null\n\t\t\t\t? {\n\t\t\t\t\t\tvalues: [],\n\t\t\t\t\t\ttruncated: projects.metadata.completeness !== 'complete',\n\t\t\t\t\t\tmetadata: projects.metadata,\n\t\t\t\t\t}\n\t\t\t\t: { values: [], truncated: false };\n\t\t}\n\n\t\tconst { tokenWithInfo, options } = this.getApiOptions(session);\n\n\t\t// Drain one (project × filter) read fully, threading the provider's paging cursor. The scope is passed so\n\t\t// a page-level failure preserves the already-drained prefix and records a structured failure instead of\n\t\t// re-throwing.\n\t\tconst drain = async (\n\t\t\tp: AzureProjectDescriptor,\n\t\t\tfilter: { assigneeLogins?: string[]; authorLogin?: string },\n\t\t): Promise<{\n\t\t\tissues: IssueShape[];\n\t\t\tprojectKey: string;\n\t\t\ttruncated: boolean;\n\t\t\tmetadata?: CollectionMetadata;\n\t\t}> => {\n\t\t\tconst result = await collectProviderPagedResult(\n\t\t\t\tcursor =>\n\t\t\t\t\tapi.getIssuesForAzureProject(tokenWithInfo, p.resourceName, p.name, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\t...filter,\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tsort: searchOptions?.sort,\n\t\t\t\t\t}),\n\t\t\t\t20,\n\t\t\t\t{ providerId: this.id, resourceId: p.resourceId, projectId: p.name },\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tissues: result.values.map(i => fromProviderIssue(i, this, { project: p })),\n\t\t\t\t// Azure work-item ids are organization-scoped. Include both org and project so the\n\t\t\t\t// assigned/authored passes dedupe the same item without collapsing another org's item.\n\t\t\t\tprojectKey: `${p.resourceId}:${p.id}`,\n\t\t\t\ttruncated: result.truncated ?? false,\n\t\t\t\tmetadata: result.metadata,\n\t\t\t};\n\t\t};\n\n\t\t// `includeAllAssignees` broadens to every issue in each project (any assignee, any author), so a single\n\t\t// unfiltered drain per project replaces the assigned+authored pair — an unfiltered assignee drain already\n\t\t// subsumes the authored one.\n\t\t//\n\t\t// `filters` narrows the other way: unfiltered, \"my issues\" here is assigned ∪ authored, which is wider than\n\t\t// `assignee:@me`. Selecting the drains is the only correct place to narrow — dropping authored-only items\n\t\t// from the returned page would leave them counted in the per-project paging that produced it.\n\t\tconst filters = searchOptions?.filters;\n\t\tconst wantAssigned = !filters?.length || filters.includes(IssueFilter.Assignee);\n\t\tconst wantAuthored = !filters?.length || filters.includes(IssueFilter.Author);\n\t\tconst outcomes = await Promise.all(\n\t\t\tprojects.values.flatMap(p => {\n\t\t\t\tif (searchOptions?.includeAllAssignees) return [drain(p, {})];\n\n\t\t\t\tconst drains = [];\n\t\t\t\tif (wantAssigned) {\n\t\t\t\t\tdrains.push(drain(p, { assigneeLogins: [user.username!] }));\n\t\t\t\t}\n\t\t\t\tif (wantAuthored) {\n\t\t\t\t\tdrains.push(drain(p, { authorLogin: user.username! }));\n\t\t\t\t}\n\t\t\t\treturn drains;\n\t\t\t}),\n\t\t);\n\n\t\tconst issuesById = new Map<string, IssueShape>();\n\t\tlet truncated = projects.metadata != null && projects.metadata.completeness !== 'complete';\n\t\tlet drainMetadata: CollectionMetadata | undefined;\n\t\tfor (const outcome of outcomes) {\n\t\t\tif (outcome.truncated) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t\tif (outcome.metadata != null) {\n\t\t\t\tdrainMetadata = mergeCollectionMetadata(drainMetadata, outcome.metadata);\n\t\t\t}\n\n\t\t\tfor (const issue of outcome.issues) {\n\t\t\t\tconst key = `${outcome.projectKey}:${issue.nodeId ?? issue.id}`;\n\t\t\t\tif (!issuesById.has(key)) {\n\t\t\t\t\tissuesById.set(key, issue);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst metadata: CollectionMetadata | undefined = mergeCollectionMetadata(\n\t\t\tfailures.length > 0 ? { completeness: 'partial', failures: failures } : undefined,\n\t\t\tmergeCollectionMetadata(drainMetadata, projects.metadata),\n\t\t);\n\n\t\treturn { values: [...issuesById.values()], truncated: truncated, metadata: metadata };\n\t}\n\n\tprotected override async providerOnConnect(): Promise<void> {\n\t\tif (this._session == null) return;\n\n\t\tconst canHydrateStoredProjects = (metadata: CollectionMetadata | undefined): boolean =>\n\t\t\tmetadata == null || metadata.completeness === 'complete';\n\n\t\tconst storedAccount = this.ctx.storage.get(`azure:${this._session.accessToken}:account`);\n\t\tconst storedOrganizations = this.ctx.storage.get(`azure:${this._session.accessToken}:organizations`);\n\t\tconst storedProjects = this.ctx.storage.get(`azure:${this._session.accessToken}:projects`);\n\t\tlet account: Account | undefined = storedAccount?.data ? { ...storedAccount.data, provider: this } : undefined;\n\n\t\tlet organizations = storedOrganizations?.data?.map((o: AzureOrganizationDescriptor) => ({ ...o }));\n\n\t\tconst storedProjectsData = storedProjects?.data as\n\t\t\t| ProviderApiCollectionResult<AzureProjectDescriptor>\n\t\t\t| AzureProjectDescriptor[]\n\t\t\t| undefined;\n\t\tlet projects: ProviderApiCollectionResult<AzureProjectDescriptor> | undefined;\n\t\tif (!Array.isArray(storedProjectsData) && Array.isArray(storedProjectsData?.values)) {\n\t\t\tconst hydrated = {\n\t\t\t\tvalues: storedProjectsData.values.map((p: AzureProjectDescriptor) => ({ ...p })),\n\t\t\t\t...(storedProjectsData.metadata != null ? { metadata: storedProjectsData.metadata } : {}),\n\t\t\t};\n\t\t\tif (canHydrateStoredProjects(hydrated.metadata)) {\n\t\t\t\tprojects = hydrated;\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 organizations and projects and accounts when our session changes\n\t\t\t\tawait this.ctx.storage.deleteWithPrefix('azure');\n\t\t\t\tawait this.ctx.storage.store(`azure:${this._session.accessToken}: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\n\t\tthis._accounts ??= new Map<string, Account | undefined>();\n\t\tthis._accounts.set(this._session.accessToken, account);\n\n\t\tif (storedOrganizations == null) {\n\t\t\torganizations = await this.getProviderResourcesForUser(this._session, true);\n\t\t\tawait this.ctx.storage.store(`azure:${this._session.accessToken}:organizations`, {\n\t\t\t\tv: 1,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdata: organizations,\n\t\t\t});\n\t\t}\n\n\t\tthis._organizations ??= new Map<string, AzureOrganizationDescriptor[] | undefined>();\n\t\tthis._organizations.set(this._session.accessToken, organizations);\n\n\t\tif (projects == null && organizations?.length) {\n\t\t\tprojects = await this.getProviderProjectsForResources(this._session, organizations);\n\t\t\tif (projects != null && canHydrateStoredProjects(projects.metadata)) {\n\t\t\t\tawait this.ctx.storage.store(`azure:${this._session.accessToken}:projects`, {\n\t\t\t\t\tv: 2,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\tdata: projects,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tawait this.ctx.storage.delete(`azure:${this._session.accessToken}:projects`);\n\t\t\t}\n\t\t}\n\n\t\tthis._projects ??= new Map<string, AzureProjectDescriptor[] | undefined>();\n\t\tif (projects != null && canHydrateStoredProjects(projects.metadata)) {\n\t\t\tfor (const project of projects.values) {\n\t\t\t\tconst projectKey = `${this._session.accessToken}:${project.resourceId}`;\n\t\t\t\tconst projects = this._projects.get(projectKey);\n\t\t\t\tif (projects == null) {\n\t\t\t\t\tthis._projects.set(projectKey, [project]);\n\t\t\t\t} else if (!projects.some(p => p.id === project.id)) {\n\t\t\t\t\tprojects.push(project);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tthis._organizations = undefined;\n\t\tthis._projects = undefined;\n\t\tthis._accounts = undefined;\n\t}\n\n\tprotected fromAzureProviderPullRequest(\n\t\tazurePullRequest: ProviderPullRequest,\n\t\trepoDescriptors: AzureRemoteRepositoryDescriptor[],\n\t\tprojectDescriptors: AzureProjectDescriptor[],\n\t): PullRequest {\n\t\tconst baseRepoDescriptor = repoDescriptors.find(r => r.id === azurePullRequest.repository.id);\n\t\tconst headRepoDescriptor =\n\t\t\tazurePullRequest.headRepository != null\n\t\t\t\t? repoDescriptors.find(r => r.id === azurePullRequest.headRepository!.id)\n\t\t\t\t: undefined;\n\t\tlet project: AzureProjectDescriptor | undefined;\n\t\tif (baseRepoDescriptor != null) {\n\t\t\tazurePullRequest.repository.remoteInfo = {\n\t\t\t\t...azurePullRequest.repository.remoteInfo,\n\t\t\t\tcloneUrlHTTPS: baseRepoDescriptor.cloneUrlHttps ?? '',\n\t\t\t\tcloneUrlSSH: baseRepoDescriptor.cloneUrlSsh ?? '',\n\t\t\t};\n\t\t}\n\n\t\tif (headRepoDescriptor != null) {\n\t\t\tazurePullRequest.headRepository = {\n\t\t\t\t...azurePullRequest.headRepository,\n\t\t\t\tid: azurePullRequest.headRepository?.id ?? headRepoDescriptor.id,\n\t\t\t\tname: azurePullRequest.headRepository?.name ?? headRepoDescriptor.name,\n\t\t\t\towner: {\n\t\t\t\t\tlogin: azurePullRequest.headRepository?.owner.login ?? headRepoDescriptor.resourceName,\n\t\t\t\t},\n\t\t\t\tremoteInfo: {\n\t\t\t\t\t...azurePullRequest.headRepository?.remoteInfo,\n\t\t\t\t\tcloneUrlHTTPS: headRepoDescriptor.cloneUrlHttps ?? '',\n\t\t\t\t\tcloneUrlSSH: headRepoDescriptor.cloneUrlSsh ?? '',\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (baseRepoDescriptor?.projectName != null) {\n\t\t\tproject = projectDescriptors.find(\n\t\t\t\tp => p.resourceName === baseRepoDescriptor.resourceName && p.name === baseRepoDescriptor.projectName,\n\t\t\t);\n\t\t}\n\t\treturn fromProviderPullRequest(azurePullRequest, this, { project: project });\n\t}\n}\n\nconst cloudMetadata = providersMetadata[GitCloudHostIntegrationId.AzureDevOps];\nconst cloudAuthProvider = Object.freeze({ id: cloudMetadata.id, scopes: cloudMetadata.scopes });\n\nexport class AzureDevOpsIntegration extends AzureDevOpsIntegrationBase<GitCloudHostIntegrationId.AzureDevOps> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = cloudAuthProvider;\n\treadonly id = GitCloudHostIntegrationId.AzureDevOps;\n\tprotected readonly key = this.id;\n\treadonly name: string = 'Azure DevOps';\n\tget domain(): string {\n\t\treturn cloudMetadata.domain;\n\t}\n\tprotected override get apiBaseUrl(): string {\n\t\treturn 'https://dev.azure.com';\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 get apiBaseUrl(): string {\n\t\tconst protocol = this._session?.protocol ?? 'https:';\n\t\treturn `${protocol}//${this.domain}`;\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.apiBaseUrl },\n\t\t};\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.apiBaseUrl)\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":"azureDevOps.js","names":[],"sources":["../../../../../plus/integrations/src/providers/azureDevOps.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScope, 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\tPullRequestState,\n\tPullRequestStateFilter,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { mapSettledBounded } from '@gitlens/utils/promise.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type {\n\tAuthenticationSessionLike as AuthenticationSession,\n\tProviderAuthenticationSession,\n\tTokenWithInfo,\n} from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport type { ProviderRefusal } from '../collectionMetadata.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport type { GitSelfManagedHostIntegrationId } from '../constants.js';\nimport { GitCloudHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport type { SearchMyPullRequestsOptions, SearchPullRequestsOptions } from '../models/gitHostIntegration.js';\nimport { GitHostIntegration } from '../models/gitHostIntegration.js';\nimport type { AccountWideIssuesResult, SearchMyIssuesOptions } from '../models/integration.js';\nimport type { ProviderWarningCause, ProviderWarningScope } from '../results.js';\nimport { decodePathSegment } from '../utils/domain.utils.js';\nimport type {\n\tAzureOrganizationDescriptor,\n\tAzureProjectDescriptor,\n\tAzureProjectInputDescriptor,\n\tAzureRemoteRepositoryDescriptor,\n\tAzureRepositoryDescriptor,\n} from './azure/models.js';\nimport type {\n\tProviderApiCollectionResult,\n\tProviderApiPagedResult,\n\tProviderAzureResource,\n\tProviderHierarchyResult,\n\tProviderOrganization,\n\tProviderPullRequest,\n\tProviderRepoInput,\n\tProviderRepository,\n} from './models.js';\nimport {\n\tfromProviderIssue,\n\tfromProviderPullRequest,\n\tgetProviderPullRequestIdentity,\n\tIssueFilter,\n\tproviderPullRequestMatchesSearch,\n\tprovidersMetadata,\n\tPullRequestFilter,\n\ttoProviderPullRequestStates,\n} from './models.js';\nimport {\n\tcollectProviderPagedResult,\n\tflatSettledResultsOrThrow,\n\tmergeCollectionMetadata,\n} from './utils/providerPaging.js';\n\nexport function getAzureRepositoryIdentity(repo: Pick<AzureRepositoryDescriptor, 'owner' | 'name' | 'project'>): {\n\tresourceName: string;\n\tprojectName?: string;\n\trepositoryName: string;\n} {\n\tconst match = /^([^/]+)\\/_git\\/([^/]+)$/i.exec(repo.name);\n\treturn {\n\t\tresourceName: repo.owner,\n\t\tprojectName: repo.project ?? match?.[1],\n\t\trepositoryName: match?.[2] ?? repo.name,\n\t};\n}\n\nexport function getAzureRepositoryApiBaseUrl(\n\tbaseUrl: string,\n\trepo: Pick<AzureRepositoryDescriptor, 'owner' | 'virtualDirectory'>,\n): string {\n\tconst directory = repo.virtualDirectory?.split('/');\n\tif (directory?.some(segment => !segment || segment === '.' || segment === '..')) {\n\t\tthrow new Error(`Invalid Azure virtual directory '${repo.virtualDirectory}'.`);\n\t}\n\n\tconst url = new URL(baseUrl);\n\tconst toUrl = (segments: string[]) => `${url.protocol}//${url.host}${segments.map(s => `/${s}`).join('')}`;\n\tconst configured = url.pathname.split('/').filter(Boolean);\n\t// A connection addressed at the host root takes the repository's own directory.\n\tif (!configured.length) return toUrl(directory?.map(encodeURIComponent) ?? []);\n\n\t// Otherwise the address is the installation, optionally followed by the repository's collection, which\n\t// requests append themselves (as the owner). IIS serves these paths case-insensitively.\n\tconst namesCollection = sameAzurePathSegment(configured.at(-1)!, repo.owner);\n\tconst installation = namesCollection ? configured.slice(0, -1) : configured;\n\tif (directory == null) return toUrl(installation);\n\n\tfor (const candidate of namesCollection ? [configured, installation] : [configured]) {\n\t\tif (candidate.length === directory.length && candidate.every((s, i) => sameAzurePathSegment(s, directory[i]))) {\n\t\t\treturn toUrl(candidate);\n\t\t}\n\t}\n\tthrow new Error('Azure repository virtual directory does not match the configured installation');\n}\n\nfunction sameAzurePathSegment(encoded: string, value: string): boolean {\n\treturn decodePathSegment(encoded).toLowerCase() === value.toLowerCase();\n}\n\n/**\n * Whether two collection or project names name the same scope. Azure DevOps treats them case-insensitively, so a\n * name typed by hand must still select the collection or project discovery reported.\n */\nexport function sameAzureName(a: string, b: string): boolean {\n\treturn a.toLowerCase() === b.toLowerCase();\n}\n\n/** The distinct names in `names`, compared as Azure compares them, keeping the first spelling of each. */\nexport function uniqueAzureNames(names: readonly string[]): string[] {\n\tconst unique: string[] = [];\n\tfor (const name of names) {\n\t\tif (!unique.some(n => sameAzureName(n, name))) {\n\t\t\tunique.push(name);\n\t\t}\n\t}\n\treturn unique;\n}\n\n/**\n * Matches an org/project descriptor against a caller-supplied name, mirroring the facade's own\n * key/id/name comparison so `listIssuesPage({ org, project })` narrows on the same identifiers a consumer\n * already got back from `listOrgs`/`listProjects`.\n */\nfunction azureResourceMatches(resource: { key?: string; id?: string; name?: string }, value: string): boolean {\n\treturn resource.key === value || resource.id === value || resource.name === value;\n}\n\nexport abstract class AzureDevOpsIntegrationBase<\n\tTIntegrationId extends GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer,\n\tTRepositoryDescriptor extends AzureRepositoryDescriptor = AzureRepositoryDescriptor,\n> extends GitHostIntegration<TIntegrationId, TRepositoryDescriptor> {\n\tprotected abstract apiBaseUrlFor(session: ProviderAuthenticationSession): string;\n\tprotected getApiOptions(\n\t\tsession: ProviderAuthenticationSession,\n\t\tdoNotConvertToPat: boolean = false,\n\t): {\n\t\ttokenWithInfo: TokenWithInfo<TIntegrationId>;\n\t\toptions: { isPAT: boolean; baseUrl?: string };\n\t} {\n\t\t// The secret is handed over raw: `isPAT` tells provider-apis to send it as an HTTP Basic credential, which\n\t\t// it encodes itself as `base64(':' + token)`. Pre-encoding here would be encoded a second time and refused\n\t\t// by Azure DevOps. `getCurrentUser` is the one read that needs a bearer token, and passes\n\t\t// `doNotConvertToPat` to get one.\n\t\tconst usePat = !doNotConvertToPat;\n\t\tconst tokenWithInfo = toTokenWithInfo<TIntegrationId>(this.id, session);\n\t\treturn {\n\t\t\ttokenWithInfo: tokenWithInfo,\n\t\t\toptions: { isPAT: usePat },\n\t\t};\n\t}\n\n\t/**\n\t * The options for a request addressed below one collection (an Azure DevOps Services organization).\n\t *\n\t * The same as {@link getApiOptions} unless a self-managed address already names the collection, which requests\n\t * append themselves: see the server override, which keeps it from being applied twice.\n\t */\n\tprotected getCollectionApiOptions(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_collection: string,\n\t): { isPAT: boolean; baseUrl?: string } {\n\t\treturn this.getApiOptions(session).options;\n\t}\n\n\t/** The base a request or link below `collection` appends the collection to; see {@link getCollectionApiOptions}. */\n\tprotected collectionApiBaseUrl(session: ProviderAuthenticationSession, _collection: string): string {\n\t\treturn this.apiBaseUrlFor(session);\n\t}\n\n\t/** Reads the organizations (Azure DevOps Server: the collections) the account can see. */\n\tprotected async requestResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tuserId: string,\n\t): Promise<ProviderAzureResource[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo, options } = this.getApiOptions(session);\n\t\treturn api.getAzureResourcesForUser(tokenWithInfo, userId, options);\n\t}\n\n\t/**\n\t * The identity id a pull-request creator/reviewer filter must carry for `collection`.\n\t *\n\t * On Azure DevOps Services that is the account id, one per user. The server override resolves it per collection,\n\t * since Azure DevOps Server gives one person a different id in each collection than at the server level.\n\t */\n\tprotected async getFilterUserId(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_collection: string,\n\t): Promise<string | undefined> {\n\t\treturn (await this.getProviderCurrentAccount(session))?.id;\n\t}\n\n\t/**\n\t * What the discovery caches (account, organizations, projects, and their stored copies) are keyed by. Azure DevOps\n\t * Services has a single address, so the credential alone identifies what it discovers; the server override adds\n\t * the installation address.\n\t */\n\tprotected discoveryKey(session: ProviderAuthenticationSession): string {\n\t\treturn session.accessToken;\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 key = this.discoveryKey(session);\n\t\tthis._accounts ??= new Map<string, Account | undefined>();\n\n\t\tconst cachedAccount = this._accounts.get(key);\n\t\tif (cachedAccount == null) {\n\t\t\tconst user = await this._requestForCurrentUser(session);\n\t\t\tthis._accounts.set(key, user);\n\t\t}\n\n\t\treturn this._accounts.get(key);\n\t}\n\n\tprotected async _requestForCurrentUser(session: ProviderAuthenticationSession): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo, options } = this.getApiOptions(session, true);\n\t\tconst user = await api.getCurrentUser(tokenWithInfo, options);\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\t/**\n\t * The profile request the account cache would otherwise answer; see `IntegrationBase.validateCredential`. No\n\t * account proves nothing, since Azure DevOps Server's request answers every failure but a refusal that way.\n\t */\n\tprotected override async validateCredential(session: ProviderAuthenticationSession): Promise<void> {\n\t\tif ((await this._requestForCurrentUser(session)) == null) {\n\t\t\tthrow new Error('Azure DevOps did not confirm the credential');\n\t\t}\n\t}\n\n\t/**\n\t * Names a confirmed credential's refusal from the answers Azure DevOps gives, captured live (#5890):\n\t * - `VS403463` in the explanation is a Conditional Access policy;\n\t * - a typed `UnauthorizedRequestException` body is an organization or project the account has no access to;\n\t * - a `401` to an OAuth token that explains nothing (sent as Basic), or only `TF400813` (sent as Bearer), is an\n\t * organization whose \"Third-party application access via OAuth\" policy is off. It is the same answer an\n\t * expired token gets, which is why this is only asked once the credential passed. The policy does not govern\n\t * personal access tokens, so a PAT is never given it.\n\t *\n\t * Any other explanation is left unnamed, so the warning keeps Azure's own words rather than a guess.\n\t */\n\tprotected override describeRefusal(\n\t\tsession: ProviderAuthenticationSession,\n\t\trefusal: ProviderRefusal,\n\t\tscope: ProviderWarningScope,\n\t): ProviderWarningCause | undefined {\n\t\tconst code = /\\b(?:TF|VS)\\d{6}\\b/.exec(refusal.detail ?? '')?.[0];\n\t\tif (code === 'VS403463') return { reason: 'conditional-access', code: code };\n\t\tif (refusal.typeKey === 'UnauthorizedRequestException') {\n\t\t\treturn { reason: 'access-denied', ...(code != null ? { code: code } : {}) };\n\t\t}\n\t\tif (\n\t\t\tthis.id !== GitCloudHostIntegrationId.AzureDevOps ||\n\t\t\tsession.type !== 'oauth' ||\n\t\t\trefusal.status !== 401 ||\n\t\t\trefusal.typeKey != null ||\n\t\t\t(refusal.detail != null && code !== 'TF400813')\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// The organization's name, for the policy page, comes from the discovery the failure was recorded under. Most\n\t\t// reads record the organization's id, the repo-scoped ones its name.\n\t\tconst org =\n\t\t\tscope.resourceId != null\n\t\t\t\t? this._organizations\n\t\t\t\t\t\t?.get(this.discoveryKey(session))\n\t\t\t\t\t\t?.find(o => o.id === scope.resourceId || o.name === scope.resourceId)?.name\n\t\t\t\t: undefined;\n\t\treturn {\n\t\t\treason: 'oauth-app-not-allowed',\n\t\t\t...(code != null ? { code: code } : {}),\n\t\t\t...(org != null\n\t\t\t\t? {\n\t\t\t\t\t\tremedyUrl: `${this.apiBaseUrlFor(session)}/${encodeURIComponent(org)}/_settings/organizationPolicy`,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\tprivate _organizations: Map<string, AzureOrganizationDescriptor[] | undefined> | undefined;\n\tprotected async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<AzureOrganizationDescriptor[] | undefined> {\n\t\tthis._organizations ??= new Map<string, AzureOrganizationDescriptor[] | undefined>();\n\t\tconst key = this.discoveryKey(session);\n\t\tconst cachedResources = this._organizations.get(key);\n\n\t\tif (cachedResources == null || force) {\n\t\t\tconst account = await this.getProviderCurrentAccount(session);\n\t\t\tif (account?.id == null) return undefined;\n\n\t\t\tconst resources = await this.requestResourcesForUser(session, account.id);\n\t\t\tthis._organizations.set(key, resources != null ? resources.map(r => ({ ...r, key: r.id })) : undefined);\n\t\t}\n\n\t\treturn this._organizations.get(key);\n\t}\n\n\tprivate _projects: Map<string, AzureProjectDescriptor[] | undefined> | undefined;\n\t/**\n\t * Discovers (and caches) each resource's projects. Only resources whose drain completed cleanly are cached;\n\t * a rejected or backstop-truncated drain is left uncached (retried next call). When `failures` is supplied,\n\t * a rejected resource is recorded there as a structured {@link CollectionScopeFailure} so an account-wide\n\t * caller can surface the incomplete project set (a whole org's PRs/issues silently missing otherwise) as a\n\t * scope-aware warning + `fetchFailed` rather than an all-pages success over a hole.\n\t */\n\tprotected async getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: AzureOrganizationDescriptor[],\n\t\tforce: boolean = false,\n\t\tfailures?: CollectionScopeFailure[],\n\t): Promise<ProviderApiCollectionResult<AzureProjectDescriptor>> {\n\t\tthis._projects ??= new Map<string, AzureProjectDescriptor[] | undefined>();\n\t\tconst discoveryKey = this.discoveryKey(session);\n\n\t\tlet resourcesWithoutProjects = [];\n\t\tif (force) {\n\t\t\tresourcesWithoutProjects = resources;\n\t\t} else {\n\t\t\tfor (const resource of resources) {\n\t\t\t\tconst resourceKey = `${discoveryKey}:${resource.id}`;\n\t\t\t\tconst cachedProjects = this._projects.get(resourceKey);\n\t\t\t\tif (cachedProjects == null) {\n\t\t\t\t\tresourcesWithoutProjects.push(resource);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst allProjects: AzureProjectDescriptor[] = [];\n\t\tlet resultMetadata: CollectionMetadata | undefined;\n\n\t\tif (resourcesWithoutProjects.length > 0) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst { tokenWithInfo } = this.getApiOptions(session);\n\t\t\t// The projects API is paginated; a single call would drop every project past the first page (and\n\t\t\t// with it their repos and PRs). Drain all pages per resource, threading the returned cursor.\n\t\t\t// Per-resource (not a shared flatSettled) so a resource whose drain was truncated (hit the paging\n\t\t\t// backstop) or rejected is NOT cached — caching a partial list here would make every later repo/PR/\n\t\t\t// issue read for that org silently inherit an incomplete project set. Leaving it uncached means the\n\t\t\t// next call retries it. The scope is passed so a page-level failure preserves the prefix already\n\t\t\t// fetched and records a structured failure instead of re-throwing.\n\t\t\tconst drains = await Promise.allSettled(\n\t\t\t\tresourcesWithoutProjects.map(async resource => ({\n\t\t\t\t\tresource: resource,\n\t\t\t\t\tresult: await collectProviderPagedResult(\n\t\t\t\t\t\tcursor =>\n\t\t\t\t\t\t\tapi.getAzureProjectsForResource(tokenWithInfo, resource.name, {\n\t\t\t\t\t\t\t\t...this.getCollectionApiOptions(session, resource.name),\n\t\t\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t20,\n\t\t\t\t\t\t{ providerId: this.id, resourceId: resource.id },\n\t\t\t\t\t),\n\t\t\t\t})),\n\t\t\t);\n\n\t\t\t// `allSettled` preserves order, so `drains[i]` is `resourcesWithoutProjects[i]`.\n\t\t\tdrains.forEach((drain, i) => {\n\t\t\t\t// A rejected resource drain contributes nothing and is left uncached (retried next call). Record\n\t\t\t\t// it as a structured failure so an account-wide caller can warn on the org whose projects (and\n\t\t\t\t// thus PRs/issues) are missing, instead of silently narrowing the read.\n\t\t\t\tif (drain.status !== 'fulfilled') {\n\t\t\t\t\tconst resource = resourcesWithoutProjects[i];\n\t\t\t\t\tconst failure = toCollectionScopeFailure(\n\t\t\t\t\t\t{ providerId: this.id, resourceId: resource.id },\n\t\t\t\t\t\tdrain.reason,\n\t\t\t\t\t);\n\t\t\t\t\tfailures?.push(failure);\n\t\t\t\t\tresultMetadata = mergeCollectionMetadata(resultMetadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [failure],\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst { resource, result } = drain.value;\n\t\t\t\tconst projects = result.values\n\t\t\t\t\t.filter(p => p.namespace === resource.name)\n\t\t\t\t\t.map(p => ({\n\t\t\t\t\t\tid: p.id,\n\t\t\t\t\t\tname: p.name,\n\t\t\t\t\t\tresourceId: resource.id,\n\t\t\t\t\t\tresourceName: resource.name,\n\t\t\t\t\t\tkey: p.id,\n\t\t\t\t\t}));\n\n\t\t\t\tif (result.metadata != null) {\n\t\t\t\t\tresultMetadata = mergeCollectionMetadata(resultMetadata, result.metadata);\n\t\t\t\t}\n\n\t\t\t\tconst metadataIncomplete = result.metadata != null && result.metadata.completeness !== 'complete';\n\t\t\t\tif (result.truncated || metadataIncomplete) {\n\t\t\t\t\t// A truncated drain is an incomplete project set; include its partial values in the current\n\t\t\t\t\t// result but don't cache it as if complete. Add a structured failure for the truncation unless\n\t\t\t\t\t// the drain already recorded a page-level failure for this resource.\n\t\t\t\t\tif (\n\t\t\t\t\t\tresult.truncated &&\n\t\t\t\t\t\t!result.metadata?.failures?.some(\n\t\t\t\t\t\t\tf => f.scope?.resourceId === resource.id && f.kind !== 'unknown',\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst failure = toCollectionScopeFailure(\n\t\t\t\t\t\t\t{ providerId: this.id, resourceId: resource.id },\n\t\t\t\t\t\t\tnew Error('Project discovery was truncated before all pages were read'),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tfailures?.push(failure);\n\t\t\t\t\t\tresultMetadata = mergeCollectionMetadata(resultMetadata, {\n\t\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\t\tfailures: [failure],\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tallProjects.push(...projects);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._projects!.set(`${discoveryKey}:${resource.id}`, projects);\n\t\t\t});\n\t\t}\n\n\t\tconst cachedProjects = resources.reduce<AzureProjectDescriptor[]>((projects, resource) => {\n\t\t\tconst resourceProjects = this._projects!.get(`${discoveryKey}:${resource.id}`);\n\t\t\tif (resourceProjects != null) {\n\t\t\t\tprojects.push(...resourceProjects);\n\t\t\t}\n\t\t\treturn projects;\n\t\t}, []);\n\t\tallProjects.push(...cachedProjects);\n\n\t\tconst projectsByIdentity = new Map<string, AzureProjectDescriptor>();\n\t\tfor (const project of allProjects) {\n\t\t\tconst identity = `${project.resourceId}:${project.id}`;\n\t\t\tif (!projectsByIdentity.has(identity)) {\n\t\t\t\tprojectsByIdentity.set(identity, project);\n\t\t\t}\n\t\t}\n\t\tconst values = [...projectsByIdentity.values()];\n\t\treturn resultMetadata != null ? { values: values, metadata: resultMetadata } : { values: values };\n\t}\n\n\tprivate async getRepoDescriptorsForProjects(\n\t\tsession: ProviderAuthenticationSession,\n\t\tprojects: AzureProjectDescriptor[],\n\t): Promise<Map<string, AzureRemoteRepositoryDescriptor[] | undefined>> {\n\t\tconst descriptors = new Map<string, AzureRemoteRepositoryDescriptor[] | undefined>();\n\t\tif (projects.length === 0) return descriptors;\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo } = this.getApiOptions(session);\n\t\tawait Promise.all(\n\t\t\tprojects.map(async project => {\n\t\t\t\tconst repos = (\n\t\t\t\t\tawait api.getReposForAzureProject(\n\t\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\t\tproject.resourceName,\n\t\t\t\t\t\tproject.name,\n\t\t\t\t\t\tthis.getCollectionApiOptions(session, project.resourceName),\n\t\t\t\t\t)\n\t\t\t\t)?.values;\n\t\t\t\tif (repos != null && repos.length > 0) {\n\t\t\t\t\tdescriptors.set(\n\t\t\t\t\t\tproject.id,\n\t\t\t\t\t\trepos.map(r => ({\n\t\t\t\t\t\t\tid: r.id,\n\t\t\t\t\t\t\tnodeId: r.graphQLId ?? undefined,\n\t\t\t\t\t\t\tresourceName: project.resourceName,\n\t\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\t\tprojectName: project.name,\n\t\t\t\t\t\t\turl: r.webUrl ?? undefined,\n\t\t\t\t\t\t\tcloneUrlHttps: r.httpsUrl ?? undefined,\n\t\t\t\t\t\t\tcloneUrlSsh: r.sshUrl ?? undefined,\n\t\t\t\t\t\t\tkey: r.id,\n\t\t\t\t\t\t})),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\treturn descriptors;\n\t}\n\n\tprotected override async getProviderOrganizationsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null) return undefined;\n\n\t\treturn {\n\t\t\tvalues: orgs.map(o => ({\n\t\t\t\tid: o.id,\n\t\t\t\tproviderId: this.id,\n\t\t\t\tname: o.name,\n\t\t\t\turl: `${this.collectionApiBaseUrl(session, o.name)}/${o.name}`,\n\t\t\t})),\n\t\t};\n\t}\n\n\tprotected override async getProviderProjectsForOrg(\n\t\tsession: ProviderAuthenticationSession,\n\t\torg?: string,\n\t): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {\n\t\t// Azure is the one git host with a project tier: repos live under org (resource) → project. Enumerate\n\t\t// the user's orgs (optionally scoped to `org`), read their projects, and surface each as an org-shaped\n\t\t// entry so the ProviderBackend facade can list them uniformly.\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null) return undefined;\n\t\tif (orgs.length === 0) {\n\t\t\treturn { values: [], paging: { cursor: '{}', more: false } };\n\t\t}\n\n\t\tconst scopedOrgs = org != null ? orgs.filter(o => o.name === org || o.id === org) : orgs;\n\t\tif (scopedOrgs.length === 0) return { values: [] };\n\n\t\tconst projects = await this.getProviderProjectsForResources(session, scopedOrgs);\n\t\tif (projects.values.length === 0 && projects.metadata == null) return { values: [] };\n\n\t\treturn {\n\t\t\tvalues: projects.values.map(p => ({\n\t\t\t\tid: p.id,\n\t\t\t\tproviderId: this.id,\n\t\t\t\tname: p.name,\n\t\t\t\torg: p.resourceName,\n\t\t\t\turl: `${this.collectionApiBaseUrl(session, p.resourceName)}/${p.resourceName}/${p.name}`,\n\t\t\t})),\n\t\t\t...(projects.metadata != null ? { metadata: projects.metadata } : {}),\n\t\t};\n\t}\n\n\t/**\n\t * With `options.project`, returns one page of that project's repos (follow `paging.cursor` to page).\n\t * Without a project it fans out across every project under `org` and returns them all at once — there's\n\t * no single cursor to page a parallel merge — skipping any project that fails to list rather than\n\t * failing the whole org. If any successful project drain hits the defensive page backstop, the merged\n\t * result is marked `truncated` without exposing a synthetic cursor.\n\t */\n\tprotected override async getProviderRepositoriesForOrg(\n\t\tsession: ProviderAuthenticationSession,\n\t\torg: string,\n\t\toptions?: { project?: string; cursor?: string },\n\t): Promise<ProviderHierarchyResult<ProviderRepository> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo } = this.getApiOptions(session);\n\t\tconst apiOptions = this.getCollectionApiOptions(session, org);\n\n\t\tif (options?.project) {\n\t\t\treturn api.getReposForAzureProject(tokenWithInfo, org, options.project, {\n\t\t\t\t...apiOptions,\n\t\t\t\tcursor: options.cursor,\n\t\t\t});\n\t\t}\n\n\t\tconst orgDescriptor = (await this.getProviderResourcesForUser(session))?.find(o => o.name === org);\n\t\tif (orgDescriptor == null) return undefined;\n\n\t\tconst discoveryFailures: CollectionScopeFailure[] = [];\n\t\tconst projects = await this.getProviderProjectsForResources(session, [orgDescriptor], false, discoveryFailures);\n\t\t// An empty result is only proven-empty when discovery itself succeeded; a rejected project-discovery\n\t\t// leaves the repo set unknowable, so surface the discovery metadata rather than publishing a hole as a\n\t\t// complete list.\n\t\tif (projects.values.length === 0) {\n\t\t\treturn { values: [], metadata: projects.metadata };\n\t\t}\n\n\t\tlet repoMetadata: CollectionMetadata | undefined;\n\t\tconst results = await Promise.allSettled(\n\t\t\tprojects.values.map(p =>\n\t\t\t\tcollectProviderPagedResult(\n\t\t\t\t\tcursor =>\n\t\t\t\t\t\tapi.getReposForAzureProject(tokenWithInfo, org, p.name, { ...apiOptions, cursor: cursor }),\n\t\t\t\t\t20,\n\t\t\t\t\t{ providerId: this.id, resourceId: org, projectId: p.name },\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\n\t\tconst values: ProviderRepository[] = [];\n\t\tlet truncated = false;\n\t\tfor (const result of results) {\n\t\t\t// With a per-project scope, collectProviderPagedResult catches page-level failures itself and returns\n\t\t\t// them as metadata rather than rejecting. A rejected promise here is an unexpected internal error.\n\t\t\tif (result.status !== 'fulfilled') {\n\t\t\t\ttruncated = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvalues.push(...result.value.values);\n\t\t\tif (result.value.metadata != null) {\n\t\t\t\trepoMetadata = mergeCollectionMetadata(repoMetadata, result.value.metadata);\n\t\t\t}\n\t\t\ttruncated ||= result.value.truncated === true;\n\t\t}\n\n\t\tconst metadata = mergeCollectionMetadata(repoMetadata, projects.metadata);\n\t\treturn {\n\t\t\tvalues: values,\n\t\t\t...(metadata != null ? { metadata: metadata } : {}),\n\t\t\t...(truncated || (metadata != null && metadata.completeness !== 'complete') ? { truncated: true } : {}),\n\t\t};\n\t}\n\n\tprotected override async mergeProviderPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tpr: PullRequest,\n\t\toptions?: {\n\t\t\tmergeMethod?: PullRequestMergeMethod;\n\t\t},\n\t): Promise<boolean> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif (pr.refs == null || pr.project == null) return false;\n\n\t\tconst { tokenWithInfo } = this.getApiOptions(session);\n\t\tconst apiOptions = this.getCollectionApiOptions(session, pr.repository.owner);\n\n\t\ttry {\n\t\t\tconst merged = await api.mergePullRequest(tokenWithInfo, pr, {\n\t\t\t\t...options,\n\t\t\t\t...apiOptions,\n\t\t\t});\n\t\t\treturn merged;\n\t\t} catch (ex) {\n\t\t\tthis.showMergeErrorMessage(ex);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected showMergeErrorMessage(ex: Error): void {\n\t\tthis.ctx.hooks?.ui?.onError?.(\n\t\t\t`${ex.message}. Check branch policies, and ensure you have the necessary permissions to merge the pull request.`,\n\t\t);\n\t}\n\n\tprotected override async getProviderAccountForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<UnidentifiedAuthor | undefined> {\n\t\treturn (await this.authenticationService.apis.azure)?.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\tgetAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo),\n\t\t\toptions,\n\t\t);\n\t}\n\n\tprotected override async getProviderAccountForEmail(\n\t\t_session: AuthenticationSession,\n\t\t_repo: AzureRepositoryDescriptor,\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: AzureRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<DefaultBranch | undefined> {\n\t\treturn (await this.authenticationService.apis.azure)?.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: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo) },\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\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.azure)?.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\t{\n\t\t\t\tbaseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo),\n\t\t\t\ttype: type,\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: AzureProjectInputDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\tif (user?.username == null) return undefined;\n\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null || orgs.length === 0) return undefined;\n\n\t\tconst projects = await this.getProviderProjectsForResources(session, orgs);\n\t\tif (projects.values.length === 0) return undefined;\n\n\t\tconst matchingProject = projects.values.find(p => p.resourceName === project.owner && p.name === project.name);\n\t\tif (matchingProject == null) return undefined;\n\n\t\treturn (await this.authenticationService.apis.azure)?.getIssue(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\tmatchingProject,\n\t\t\tid,\n\t\t\t{\n\t\t\t\tbaseUrl: this.collectionApiBaseUrl(session, matchingProject.resourceName),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForBranch(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\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.azure)?.getPullRequestForBranch(\n\t\t\tthis,\n\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\trepo.owner,\n\t\t\trepo.name,\n\t\t\tbranch,\n\t\t\t{\n\t\t\t\tbaseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo),\n\t\t\t},\n\t\t);\n\t}\n\n\tprotected override async getProviderPullRequestForCommit(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\n\t\trev: string,\n\t): Promise<PullRequest | undefined> {\n\t\treturn (await this.authenticationService.apis.azure)?.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\tgetAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo),\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\tvirtualDirectory?: string;\n\t\tconnectionId?: string;\n\t}): Promise<ProviderRepository | undefined> {\n\t\tconst identity = getAzureRepositoryIdentity(repo);\n\t\tif (identity.projectName == null) return undefined;\n\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.resolveReadSession(repo.connectionId, undefined);\n\t\tif (session == null) return undefined;\n\n\t\tconst { tokenWithInfo, options } = this.getApiOptions(session);\n\t\treturn api.getRepo(tokenWithInfo, identity.resourceName, identity.repositoryName, identity.projectName, {\n\t\t\t...options,\n\t\t\tbaseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo),\n\t\t});\n\t}\n\n\tprotected override async getProviderRepositoryMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepo: AzureRepositoryDescriptor,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\treturn (await this.authenticationService.apis.azure)?.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: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo) },\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprotected override async searchProviderMyPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: AzureRepositoryDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t\toptions?: SearchMyPullRequestsOptions,\n\t): Promise<PullRequest[] | undefined> {\n\t\tconst api = await this.getProvidersApi();\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 states = toProviderPullRequestStates(options?.state);\n\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\t// Azure filters key on the identity GUID, not the display name — see getProviderMyPullRequestsForUser and the\n\t\t// repo-scoped path in gitHostIntegration.ts. The GUID itself is resolved per organization below.\n\t\tif (user?.id == null) return undefined;\n\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null || orgs.length === 0) return undefined;\n\n\t\tconst projects = await this.getProviderProjectsForResources(session, orgs);\n\t\tif (projects.values.length === 0) return undefined;\n\n\t\tconst repoDescriptors = [\n\t\t\t...((await this.getRepoDescriptorsForProjects(session, projects.values)) ?? new Map()).values(),\n\t\t]\n\t\t\t.filter(r => r != null)\n\t\t\t.flat();\n\n\t\tconst { tokenWithInfo } = this.getApiOptions(session);\n\t\t// Legacy array-returning path (Launchpad/focus view): unwrap `.values` from the SDK collection result.\n\t\t// The metadata (partial/failures) isn't surfaced here because this path's return type has no warning\n\t\t// channel; the metadata-aware ProviderBackend surface is getProviderMyPullRequestsForUser above.\n\t\t//\n\t\t// Read per organization, since both the identity the filters compare and the base the requests address are\n\t\t// the organization's own (see `getFilterUserId` and `getCollectionApiOptions`).\n\t\tconst assignedPrs: PullRequest[] = [];\n\t\tconst authoredPrs: PullRequest[] = [];\n\t\tfor (const org of uniqueAzureNames(projects.values.map(p => p.resourceName))) {\n\t\t\tconst userId = await this.getFilterUserId(session, org);\n\t\t\tif (userId == null) continue;\n\n\t\t\tconst projectInputs = projects.values\n\t\t\t\t.filter(p => sameAzureName(p.resourceName, org))\n\t\t\t\t.map(p => ({ namespace: p.resourceName, project: p.name }));\n\t\t\tconst orgOptions = this.getCollectionApiOptions(session, org);\n\t\t\tconst reads: [{ assigneeLogins?: string[]; authorLogin?: string }, PullRequest[]][] = [\n\t\t\t\t[{ assigneeLogins: [userId] }, assignedPrs],\n\t\t\t\t[{ authorLogin: userId }, authoredPrs],\n\t\t\t];\n\t\t\tfor (const [filter, into] of reads) {\n\t\t\t\tconst result = await api.getPullRequestsForAzureProjects(tokenWithInfo, projectInputs, {\n\t\t\t\t\t...orgOptions,\n\t\t\t\t\t...filter,\n\t\t\t\t\tstates: states,\n\t\t\t\t});\n\t\t\t\tinto.push(\n\t\t\t\t\t...result.values.map(pr => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects.values)),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// Azure's pull request id is only unique within a repository, so the two passes are merged by repository and\n\t\t// id: keying on the id alone let one repository's pull request hide another's with the same number.\n\t\tconst prsByIdentity = new Map<string, PullRequest>();\n\t\tfor (const pr of [...authoredPrs, ...assignedPrs]) {\n\t\t\tconst identity = `${pr.repository.owner}/${pr.repository.id || pr.repository.repo}#${pr.id}`;\n\t\t\tif (!prsByIdentity.has(identity)) {\n\t\t\t\tprsByIdentity.set(identity, pr);\n\t\t\t}\n\t\t}\n\n\t\treturn [...prsByIdentity.values()];\n\t}\n\n\tprotected override async getProviderMyPullRequestsForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions?: { state?: PullRequestStateFilter[]; cursor?: string; filters?: PullRequestFilter[] },\n\t): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\t// Azure routes authorLogin/assigneeLogins to `searchCriteria.creatorId`/`reviewerId`, which require the\n\t\t// identity GUID, not the display name — matching the repo-scoped path in gitHostIntegration.ts. Using\n\t\t// `username` here would match nothing and return zero PRs. The GUID is resolved per organization below\n\t\t// (see `getFilterUserId`); this only checks there is a user at all.\n\t\tif (user?.id == null) return undefined;\n\n\t\t// Azure PRs are org + project scoped: enumerate the user's orgs and their projects, then read authored\n\t\t// and assigned PRs across all of them. Return the raw provider shape (not the normalized model) so the\n\t\t// ProviderBackend surface stays uniform with the other providers.\n\t\tconst orgs = await this.getProviderResourcesForUser(session);\n\t\tif (orgs == null || orgs.length === 0) return undefined;\n\n\t\t// Structured per-scope failures from BOTH project discovery (a whole org dropped) and the per-project PR\n\t\t// drains, so the facade warns on the failed scope + sets `fetchFailed` instead of silently narrowing.\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tconst projects = await this.getProviderProjectsForResources(session, orgs, false, failures);\n\t\tif (projects.values.length === 0) {\n\t\t\t// Project discovery itself was incomplete (e.g. a truncated org); surface the metadata so the facade\n\t\t\t// can warn and set fetchFailed rather than reporting an empty account.\n\t\t\tconst incomplete = projects.metadata != null && projects.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...(projects.metadata != null ? { metadata: projects.metadata } : {}),\n\t\t\t};\n\t\t}\n\n\t\tconst { tokenWithInfo } = this.getApiOptions(session);\n\t\tconst states = toProviderPullRequestStates(options?.state);\n\t\tconst maxPagesPerProject = 20;\n\n\t\t// Drain each project fully (numbered pages) for both the authored and assigned reads. Azure has no\n\t\t// single cross-project cursor, so the aggregate is one page; `truncated` is set only if a project hit\n\t\t// the backstop with more pages remaining.\n\t\tlet truncated = projects.metadata != null && projects.metadata.completeness !== 'complete';\n\t\t// Drain one project's numbered pages, returning its PRs and any per-page failure. Returns per-project\n\t\t// (not mutating shared state) so the fan-out below can be settled independently: one project's read\n\t\t// failure must not discard every other project's already-drained PRs.\n\t\tconst drainProject = async (\n\t\t\tproject: { namespace: string; project: string },\n\t\t\tscope: CollectionScope,\n\t\t\tfilter: { authorLogin?: string; assigneeLogins?: string[]; reviewerId?: string },\n\t\t): Promise<{ prs: ProviderPullRequest[]; projectIdentity: string; failure?: CollectionScopeFailure }> => {\n\t\t\tconst collected: ProviderPullRequest[] = [];\n\t\t\tconst projectIdentity = `${project.namespace}/${project.project}`;\n\t\t\tlet page: number | undefined;\n\t\t\tfor (let i = 0; i < maxPagesPerProject; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await api.getPullRequestsForAzureProject(tokenWithInfo, project, {\n\t\t\t\t\t\t// Addressed below the project's own collection, applied once even when the configured address\n\t\t\t\t\t\t// already names it (the request appends the collection as `namespace`).\n\t\t\t\t\t\t...this.getCollectionApiOptions(session, project.namespace),\n\t\t\t\t\t\t...filter,\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t\tpage: page,\n\t\t\t\t\t});\n\t\t\t\t\tif (result == null) {\n\t\t\t\t\t\tif (page == null) break;\n\n\t\t\t\t\t\ttruncated = true;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tprs: collected,\n\t\t\t\t\t\t\tprojectIdentity: projectIdentity,\n\t\t\t\t\t\t\tfailure: toCollectionScopeFailure(\n\t\t\t\t\t\t\t\tscope,\n\t\t\t\t\t\t\t\tnew Error('Azure DevOps returned no page after advertising a continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tcollected.push(...result.data);\n\t\t\t\t\tif (!result.hasMore) break;\n\t\t\t\t\tif (result.nextPage == null || result.nextPage === page) {\n\t\t\t\t\t\ttruncated = true;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tprs: collected,\n\t\t\t\t\t\t\tprojectIdentity: projectIdentity,\n\t\t\t\t\t\t\tfailure: toCollectionScopeFailure(\n\t\t\t\t\t\t\t\tscope,\n\t\t\t\t\t\t\t\tnew Error('Azure DevOps returned no advancing pull request continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tpage = result.nextPage;\n\t\t\t\t\tif (i === maxPagesPerProject - 1) {\n\t\t\t\t\t\ttruncated = true;\n\t\t\t\t\t}\n\t\t\t\t} catch (ex) {\n\t\t\t\t\t// A page failure after the first page leaves the already-drained prefix intact; record the\n\t\t\t\t\t// failure at the project scope instead of re-throwing and discarding the prefix.\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tprs: collected,\n\t\t\t\t\t\tprojectIdentity: projectIdentity,\n\t\t\t\t\t\tfailure: toCollectionScopeFailure(scope, ex),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { prs: collected, projectIdentity: projectIdentity };\n\t\t};\n\n\t\t// Settle per-project failures instead of rejecting the whole sweep. `drainProject` already catches its own\n\t\t// page-level failures, so the structured failure (attributed to that project) is preserved rather than\n\t\t// re-thrown — re-throwing an auth/rate-limit rejection would discard every other project's already-drained\n\t\t// PRs. Auth/rate-limit stay actionable through the failure's kind (the facade maps it to an `auth`/`rate-limit`\n\t\t// warning + `fetchFailed`), matching the SDK's model. `failures` was declared above so project-discovery\n\t\t// failures and per-project drain failures share it.\n\t\tconst requested = options?.filters?.length ? new Set(options.filters) : undefined;\n\t\tconst wantAuthored = requested == null || requested.has(PullRequestFilter.Author);\n\t\t// Azure has no assignee concept distinct from reviewer. Both neutral relationships map to reviewerId.\n\t\tconst wantReviewed =\n\t\t\trequested == null ||\n\t\t\trequested.has(PullRequestFilter.Assignee) ||\n\t\t\trequested.has(PullRequestFilter.ReviewRequested);\n\t\tconst userIds = new Map<string, string | undefined>();\n\t\tfor (const org of uniqueAzureNames(projects.values.map(p => p.resourceName))) {\n\t\t\tuserIds.set(org.toLowerCase(), await this.getFilterUserId(session, org));\n\t\t}\n\t\tconst outcomes = await Promise.all(\n\t\t\tprojects.values.flatMap(p => {\n\t\t\t\tconst project = { namespace: p.resourceName, project: p.name };\n\t\t\t\tconst scope = { providerId: this.id, resourceId: p.resourceId, projectId: p.name };\n\t\t\t\tconst userId = userIds.get(p.resourceName.toLowerCase());\n\t\t\t\tif (userId == null) {\n\t\t\t\t\tfailures.push(\n\t\t\t\t\t\ttoCollectionScopeFailure(scope, new Error('The current user could not be resolved here')),\n\t\t\t\t\t);\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\n\t\t\t\tconst drains = [];\n\t\t\t\tif (wantAuthored) {\n\t\t\t\t\tdrains.push(drainProject(project, scope, { authorLogin: userId }));\n\t\t\t\t}\n\t\t\t\tif (wantReviewed) {\n\t\t\t\t\tdrains.push(drainProject(project, scope, { reviewerId: userId }));\n\t\t\t\t}\n\t\t\t\treturn drains;\n\t\t\t}),\n\t\t);\n\n\t\t// Azure's `pullRequestId` is not account-global. Prefer repository + PR id, then the org-qualified URL.\n\t\t// If neither is available, preserve the row instead of collapsing unrelated repositories by numeric id.\n\t\tconst prsByIdentity = new Map<string, ProviderPullRequest>();\n\t\tfor (const outcome of outcomes) {\n\t\t\tif (outcome.failure != null) {\n\t\t\t\tfailures.push(outcome.failure);\n\t\t\t}\n\t\t\tfor (const pr of outcome.prs) {\n\t\t\t\tconst key =\n\t\t\t\t\tgetProviderPullRequestIdentity(pr) ?? `project:${outcome.projectIdentity}:pull-request:${pr.id}`;\n\t\t\t\tif (!prsByIdentity.has(key)) {\n\t\t\t\t\tprsByIdentity.set(key, pr);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst metadata: CollectionMetadata | undefined = mergeCollectionMetadata(\n\t\t\tfailures.length > 0 ? { completeness: 'partial', failures: failures } : undefined,\n\t\t\tprojects.metadata,\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\tprotected override async searchProviderPullRequests(\n\t\tsession: ProviderAuthenticationSession,\n\t\tsearchQuery: string,\n\t\trepos?: AzureRepositoryDescriptor[],\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 orgs = await this.getProviderResourcesForUser(session);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\tif (orgs == null || orgs.length === 0) return undefined;\n\n\t\t// `getProviderProjectsForResources` returns a collection result ({ values, metadata }); this search\n\t\t// path only needs the resolved projects, so read `.values`.\n\t\tconst projects = (await this.getProviderProjectsForResources(session, orgs)).values;\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\tif (projects.length === 0) return undefined;\n\n\t\tconst repoDescriptorsByProject = await this.getRepoDescriptorsForProjects(session, projects);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst repoDescriptors = [...repoDescriptorsByProject.values()].filter(r => r != null).flat();\n\t\tconst requestedRepos = repos?.map(getAzureRepositoryIdentity);\n\t\tconst repoInputs =\n\t\t\trequestedRepos == null\n\t\t\t\t? undefined\n\t\t\t\t: repoDescriptors.filter(r =>\n\t\t\t\t\t\trequestedRepos.some(\n\t\t\t\t\t\t\trepo =>\n\t\t\t\t\t\t\t\trepo.resourceName === r.resourceName &&\n\t\t\t\t\t\t\t\trepo.repositoryName === r.name &&\n\t\t\t\t\t\t\t\t(repo.projectName == null || repo.projectName === r.projectName),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\tif (repoInputs?.length === 0) return [];\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst { tokenWithInfo } = this.getApiOptions(session);\n\t\tconst states = toProviderPullRequestStates(options?.include);\n\t\tconst searchScopes: { project: { namespace: string; project: string }; repo?: ProviderRepoInput }[] =\n\t\t\trepoInputs != null\n\t\t\t\t? repoInputs.flatMap(repo =>\n\t\t\t\t\t\trepo.projectName == null\n\t\t\t\t\t\t\t? []\n\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\t\tproject: { namespace: repo.resourceName, project: repo.projectName },\n\t\t\t\t\t\t\t\t\t\trepo: {\n\t\t\t\t\t\t\t\t\t\t\tid: repo.id,\n\t\t\t\t\t\t\t\t\t\t\tname: repo.name,\n\t\t\t\t\t\t\t\t\t\t\tnamespace: repo.resourceName,\n\t\t\t\t\t\t\t\t\t\t\tproject: repo.projectName,\n\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)\n\t\t\t\t: projects.map(project => ({\n\t\t\t\t\t\tproject: { namespace: project.resourceName, project: project.name },\n\t\t\t\t\t}));\n\n\t\tconst providerPullRequests = flatSettledResultsOrThrow(\n\t\t\tawait mapSettledBounded(searchScopes, providerFanOutConcurrency, async scope => {\n\t\t\t\tconst values: ProviderPullRequest[] = [];\n\t\t\t\tlet page: number | undefined;\n\t\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\tconst result = await api.getPullRequestsForAzureProject(tokenWithInfo, scope.project, {\n\t\t\t\t\t\t...this.getCollectionApiOptions(session, scope.project.namespace),\n\t\t\t\t\t\tpage: page,\n\t\t\t\t\t\trepo: scope.repo,\n\t\t\t\t\t\tstates: states,\n\t\t\t\t\t});\n\t\t\t\t\tif (result == null) break;\n\n\t\t\t\t\tvalues.push(...result.data);\n\t\t\t\t\tif (!result.hasMore || result.nextPage == null) break;\n\n\t\t\t\t\tpage = result.nextPage;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}),\n\t\t);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\treturn [...new Map(providerPullRequests.map(pr => [pr.url ?? `${pr.repository.id}:${pr.id}`, pr])).values()]\n\t\t\t.filter(pr => providerPullRequestMatchesSearch(pr, searchQuery))\n\t\t\t.map(pr => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects));\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trepos?: AzureRepositoryDescriptor[],\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, repos))?.values;\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for Azure = the user's authored + assigned work items across every project of\n\t * every org. Azure's issue read is numbered-page, so each (project × filter) read is drained to exhaustion\n\t * (bounded by a defensive per-read backstop). Unlike a silent `flatSettled`, a project read that was\n\t * truncated by the backstop or rejected outright is recorded as `truncated`, so the facade reports an\n\t * incomplete read instead of publishing a partial list as complete.\n\t *\n\t * `searchOptions.org`/`.project` narrow the fan-out server-side (each drain is already a per-project read,\n\t * so scoping just selects which projects to drain). Without this there was no way to ask for \"work items in\n\t * project P\": a consumer had to filter the account-wide page client-side, which desynchronizes the filtered\n\t * `items` from the `hasMore`/`currentPage` of the pre-filter read — a project-less page reads as \"no issues\"\n\t * while `hasMore` is still true.\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources?: ResourceDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t\tsearchOptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\n\t\tconst user = await this.getProviderCurrentAccount(session);\n\t\tif (user?.username == null) return undefined;\n\n\t\tconst allOrgs = await this.getProviderResourcesForUser(session);\n\t\tif (allOrgs == null) return undefined;\n\t\tif (allOrgs.length === 0) return { values: [], truncated: false };\n\n\t\t// Scope by org first so project discovery only fans out over the requested account. An org filter that\n\t\t// matches nothing is an empty-but-successful read, not an unsupported one: returning `undefined` here\n\t\t// would be reported as \"account-wide issue search is not supported by this provider\".\n\t\tconst orgs =\n\t\t\tsearchOptions?.org != null ? allOrgs.filter(o => azureResourceMatches(o, searchOptions.org!)) : allOrgs;\n\t\tif (orgs.length === 0) return { values: [], truncated: false };\n\n\t\t// Structured per-scope failures from BOTH project discovery (a whole org dropped) and the per-project\n\t\t// issue drains, so the facade warns on the failed scope + sets `fetchFailed` instead of narrowing silently.\n\t\tconst failures: CollectionScopeFailure[] = [];\n\t\tconst discovered = await this.getProviderProjectsForResources(session, orgs, false, failures);\n\t\tconst projects =\n\t\t\tsearchOptions?.project != null\n\t\t\t\t? {\n\t\t\t\t\t\t...discovered,\n\t\t\t\t\t\tvalues: discovered.values.filter(p => azureResourceMatches(p, searchOptions.project!)),\n\t\t\t\t\t}\n\t\t\t\t: discovered;\n\t\tif (projects.values.length === 0) {\n\t\t\t// An explicitly-scoped read that found no matching project is an empty SUCCESS: `undefined` here is\n\t\t\t// reported as \"account-wide issue search is not supported by this provider\", which would send the\n\t\t\t// consumer down a repo-scoped fallback for what is simply an empty scope. Only an incomplete discovery\n\t\t\t// (which may itself have dropped the requested project) is truncated, and its metadata is forwarded so\n\t\t\t// the facade still warns on the failed scope.\n\t\t\treturn projects.metadata != null\n\t\t\t\t? {\n\t\t\t\t\t\tvalues: [],\n\t\t\t\t\t\ttruncated: projects.metadata.completeness !== 'complete',\n\t\t\t\t\t\tmetadata: projects.metadata,\n\t\t\t\t\t}\n\t\t\t\t: { values: [], truncated: false };\n\t\t}\n\n\t\tconst { tokenWithInfo } = this.getApiOptions(session);\n\n\t\t// Drain one (project × filter) read fully, threading the provider's paging cursor. The scope is passed so\n\t\t// a page-level failure preserves the already-drained prefix and records a structured failure instead of\n\t\t// re-throwing.\n\t\tconst drain = async (\n\t\t\tp: AzureProjectDescriptor,\n\t\t\tfilter: { assigneeLogins?: string[]; authorLogin?: string },\n\t\t): Promise<{\n\t\t\tissues: IssueShape[];\n\t\t\tprojectKey: string;\n\t\t\ttruncated: boolean;\n\t\t\tmetadata?: CollectionMetadata;\n\t\t}> => {\n\t\t\tconst result = await collectProviderPagedResult(\n\t\t\t\tcursor =>\n\t\t\t\t\tapi.getIssuesForAzureProject(tokenWithInfo, p.resourceName, p.name, {\n\t\t\t\t\t\t...this.getCollectionApiOptions(session, p.resourceName),\n\t\t\t\t\t\t...filter,\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tsort: searchOptions?.sort,\n\t\t\t\t\t}),\n\t\t\t\t20,\n\t\t\t\t{ providerId: this.id, resourceId: p.resourceId, projectId: p.name },\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tissues: result.values.map(i => fromProviderIssue(i, this, { project: p })),\n\t\t\t\t// Azure work-item ids are organization-scoped. Include both org and project so the\n\t\t\t\t// assigned/authored passes dedupe the same item without collapsing another org's item.\n\t\t\t\tprojectKey: `${p.resourceId}:${p.id}`,\n\t\t\t\ttruncated: result.truncated ?? false,\n\t\t\t\tmetadata: result.metadata,\n\t\t\t};\n\t\t};\n\n\t\t// `includeAllAssignees` broadens to every issue in each project (any assignee, any author), so a single\n\t\t// unfiltered drain per project replaces the assigned+authored pair — an unfiltered assignee drain already\n\t\t// subsumes the authored one.\n\t\t//\n\t\t// `filters` narrows the other way: unfiltered, \"my issues\" here is assigned ∪ authored, which is wider than\n\t\t// `assignee:@me`. Selecting the drains is the only correct place to narrow — dropping authored-only items\n\t\t// from the returned page would leave them counted in the per-project paging that produced it.\n\t\tconst filters = searchOptions?.filters;\n\t\tconst wantAssigned = !filters?.length || filters.includes(IssueFilter.Assignee);\n\t\tconst wantAuthored = !filters?.length || filters.includes(IssueFilter.Author);\n\t\tconst outcomes = await Promise.all(\n\t\t\tprojects.values.flatMap(p => {\n\t\t\t\tif (searchOptions?.includeAllAssignees) return [drain(p, {})];\n\n\t\t\t\tconst drains = [];\n\t\t\t\tif (wantAssigned) {\n\t\t\t\t\tdrains.push(drain(p, { assigneeLogins: [user.username!] }));\n\t\t\t\t}\n\t\t\t\tif (wantAuthored) {\n\t\t\t\t\tdrains.push(drain(p, { authorLogin: user.username! }));\n\t\t\t\t}\n\t\t\t\treturn drains;\n\t\t\t}),\n\t\t);\n\n\t\tconst issuesById = new Map<string, IssueShape>();\n\t\tlet truncated = projects.metadata != null && projects.metadata.completeness !== 'complete';\n\t\tlet drainMetadata: CollectionMetadata | undefined;\n\t\tfor (const outcome of outcomes) {\n\t\t\tif (outcome.truncated) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t\tif (outcome.metadata != null) {\n\t\t\t\tdrainMetadata = mergeCollectionMetadata(drainMetadata, outcome.metadata);\n\t\t\t}\n\n\t\t\tfor (const issue of outcome.issues) {\n\t\t\t\tconst key = `${outcome.projectKey}:${issue.nodeId ?? issue.id}`;\n\t\t\t\tif (!issuesById.has(key)) {\n\t\t\t\t\tissuesById.set(key, issue);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst metadata: CollectionMetadata | undefined = mergeCollectionMetadata(\n\t\t\tfailures.length > 0 ? { completeness: 'partial', failures: failures } : undefined,\n\t\t\tmergeCollectionMetadata(drainMetadata, projects.metadata),\n\t\t);\n\n\t\treturn { values: [...issuesById.values()], truncated: truncated, metadata: metadata };\n\t}\n\n\tprotected override async providerOnConnect(): Promise<void> {\n\t\tif (this._session == null) return;\n\n\t\tconst discoveryKey = this.discoveryKey(this._session);\n\n\t\tconst canHydrateStoredProjects = (metadata: CollectionMetadata | undefined): boolean =>\n\t\t\tmetadata == null || metadata.completeness === 'complete';\n\n\t\tconst storedAccount = this.ctx.storage.get(`azure:${discoveryKey}:account`);\n\t\tconst storedOrganizations = this.ctx.storage.get(`azure:${discoveryKey}:organizations`);\n\t\tconst storedProjects = this.ctx.storage.get(`azure:${discoveryKey}:projects`);\n\t\tlet account: Account | undefined = storedAccount?.data ? { ...storedAccount.data, provider: this } : undefined;\n\n\t\tlet organizations = storedOrganizations?.data?.map((o: AzureOrganizationDescriptor) => ({ ...o }));\n\n\t\tconst storedProjectsData = storedProjects?.data as\n\t\t\t| ProviderApiCollectionResult<AzureProjectDescriptor>\n\t\t\t| AzureProjectDescriptor[]\n\t\t\t| undefined;\n\t\tlet projects: ProviderApiCollectionResult<AzureProjectDescriptor> | undefined;\n\t\tif (!Array.isArray(storedProjectsData) && Array.isArray(storedProjectsData?.values)) {\n\t\t\tconst hydrated = {\n\t\t\t\tvalues: storedProjectsData.values.map((p: AzureProjectDescriptor) => ({ ...p })),\n\t\t\t\t...(storedProjectsData.metadata != null ? { metadata: storedProjectsData.metadata } : {}),\n\t\t\t};\n\t\t\tif (canHydrateStoredProjects(hydrated.metadata)) {\n\t\t\t\tprojects = hydrated;\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 organizations and projects and accounts when our session changes\n\t\t\t\tawait this.ctx.storage.deleteWithPrefix('azure');\n\t\t\t\tawait this.ctx.storage.store(`azure:${discoveryKey}: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\n\t\tthis._accounts ??= new Map<string, Account | undefined>();\n\t\tthis._accounts.set(discoveryKey, account);\n\n\t\tif (storedOrganizations == null) {\n\t\t\torganizations = await this.getProviderResourcesForUser(this._session, true);\n\t\t\tawait this.ctx.storage.store(`azure:${discoveryKey}:organizations`, {\n\t\t\t\tv: 1,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdata: organizations,\n\t\t\t});\n\t\t}\n\n\t\tthis._organizations ??= new Map<string, AzureOrganizationDescriptor[] | undefined>();\n\t\tthis._organizations.set(discoveryKey, organizations);\n\n\t\tif (projects == null && organizations?.length) {\n\t\t\tprojects = await this.getProviderProjectsForResources(this._session, organizations);\n\t\t\tif (projects != null && canHydrateStoredProjects(projects.metadata)) {\n\t\t\t\tawait this.ctx.storage.store(`azure:${discoveryKey}:projects`, {\n\t\t\t\t\tv: 2,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\tdata: projects,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tawait this.ctx.storage.delete(`azure:${discoveryKey}:projects`);\n\t\t\t}\n\t\t}\n\n\t\tthis._projects ??= new Map<string, AzureProjectDescriptor[] | undefined>();\n\t\tif (projects != null && canHydrateStoredProjects(projects.metadata)) {\n\t\t\tfor (const project of projects.values) {\n\t\t\t\tconst projectKey = `${discoveryKey}:${project.resourceId}`;\n\t\t\t\tconst projects = this._projects.get(projectKey);\n\t\t\t\tif (projects == null) {\n\t\t\t\t\tthis._projects.set(projectKey, [project]);\n\t\t\t\t} else if (!projects.some(p => p.id === project.id)) {\n\t\t\t\t\tprojects.push(project);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tthis._organizations = undefined;\n\t\tthis._projects = undefined;\n\t\tthis._accounts = undefined;\n\t}\n\n\tprotected fromAzureProviderPullRequest(\n\t\tazurePullRequest: ProviderPullRequest,\n\t\trepoDescriptors: AzureRemoteRepositoryDescriptor[],\n\t\tprojectDescriptors: AzureProjectDescriptor[],\n\t): PullRequest {\n\t\tconst baseRepoDescriptor = repoDescriptors.find(r => r.id === azurePullRequest.repository.id);\n\t\tconst headRepoDescriptor =\n\t\t\tazurePullRequest.headRepository != null\n\t\t\t\t? repoDescriptors.find(r => r.id === azurePullRequest.headRepository!.id)\n\t\t\t\t: undefined;\n\t\tlet project: AzureProjectDescriptor | undefined;\n\t\tif (baseRepoDescriptor != null) {\n\t\t\tazurePullRequest.repository.remoteInfo = {\n\t\t\t\t...azurePullRequest.repository.remoteInfo,\n\t\t\t\tcloneUrlHTTPS: baseRepoDescriptor.cloneUrlHttps ?? '',\n\t\t\t\tcloneUrlSSH: baseRepoDescriptor.cloneUrlSsh ?? '',\n\t\t\t};\n\t\t}\n\n\t\tif (headRepoDescriptor != null) {\n\t\t\tazurePullRequest.headRepository = {\n\t\t\t\t...azurePullRequest.headRepository,\n\t\t\t\tid: azurePullRequest.headRepository?.id ?? headRepoDescriptor.id,\n\t\t\t\tname: azurePullRequest.headRepository?.name ?? headRepoDescriptor.name,\n\t\t\t\towner: {\n\t\t\t\t\tlogin: azurePullRequest.headRepository?.owner.login ?? headRepoDescriptor.resourceName,\n\t\t\t\t},\n\t\t\t\tremoteInfo: {\n\t\t\t\t\t...azurePullRequest.headRepository?.remoteInfo,\n\t\t\t\t\tcloneUrlHTTPS: headRepoDescriptor.cloneUrlHttps ?? '',\n\t\t\t\t\tcloneUrlSSH: headRepoDescriptor.cloneUrlSsh ?? '',\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (baseRepoDescriptor?.projectName != null) {\n\t\t\tproject = projectDescriptors.find(\n\t\t\t\tp => p.resourceName === baseRepoDescriptor.resourceName && p.name === baseRepoDescriptor.projectName,\n\t\t\t);\n\t\t}\n\t\treturn fromProviderPullRequest(azurePullRequest, this, { project: project });\n\t}\n}\n\nconst cloudMetadata = providersMetadata[GitCloudHostIntegrationId.AzureDevOps];\nconst cloudAuthProvider = Object.freeze({ id: cloudMetadata.id, scopes: cloudMetadata.scopes });\n\nexport class AzureDevOpsIntegration extends AzureDevOpsIntegrationBase<GitCloudHostIntegrationId.AzureDevOps> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = cloudAuthProvider;\n\treadonly id = GitCloudHostIntegrationId.AzureDevOps;\n\tprotected readonly key = this.id;\n\treadonly name: string = 'Azure DevOps';\n\tget domain(): string {\n\t\treturn cloudMetadata.domain;\n\t}\n\tprotected override apiBaseUrlFor(_session: ProviderAuthenticationSession): string {\n\t\treturn 'https://dev.azure.com';\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Emitter } from "../../../utils/event.js";
|
|
2
|
+
import { IssueSearchCriteria } from "../../../git/models/issue.js";
|
|
3
|
+
import { PullRequestSearchCriteria } from "../../../git/models/pullRequest.js";
|
|
4
|
+
import { Account } from "../../../git/models/author.js";
|
|
5
|
+
import { ProviderAuthenticationSession, TokenWithInfo } from "../authentication/models.js";
|
|
6
|
+
import { GitSelfManagedHostIntegrationId } from "../constants.js";
|
|
7
|
+
import { ProviderPullRequestCount, ProviderPullRequestSearchPage } from "../models/pullRequestReads.js";
|
|
8
|
+
import { ProvidersApi } from "./providersApi.js";
|
|
9
|
+
import { ProviderIssueSearchPage, ProviderSearchCount } from "../models/issueReads.js";
|
|
10
|
+
import { IntegrationKey } from "../models/integration.js";
|
|
11
|
+
import { ProviderAzureResource, ProviderRepoInput, ProviderReposInput } from "./models.js";
|
|
12
|
+
import { IntegrationConnectionChangeEvent } from "../integrationService.js";
|
|
13
|
+
import { IntegrationAuthenticationService } from "../authentication/integrationAuthenticationService.js";
|
|
14
|
+
import { IntegrationAuthenticationProviderDescriptor } from "../authentication/integrationAuthenticationProvider.js";
|
|
15
|
+
import { IntegrationServiceContext } from "../context.js";
|
|
16
|
+
import { AzureDevOpsIntegrationBase } from "./azureDevOps.js";
|
|
17
|
+
//#region ../plus/integrations/src/providers/azureDevOpsServer.d.ts
|
|
18
|
+
export declare class AzureDevOpsServerIntegration extends AzureDevOpsIntegrationBase<GitSelfManagedHostIntegrationId.AzureDevOpsServer> {
|
|
19
|
+
readonly domain: string;
|
|
20
|
+
readonly authProvider: IntegrationAuthenticationProviderDescriptor;
|
|
21
|
+
readonly id = GitSelfManagedHostIntegrationId.AzureDevOpsServer;
|
|
22
|
+
protected readonly key: IntegrationKey<GitSelfManagedHostIntegrationId.AzureDevOpsServer>;
|
|
23
|
+
readonly name: string;
|
|
24
|
+
constructor(ctx: IntegrationServiceContext, authenticationService: IntegrationAuthenticationService, getProvidersApi: () => Promise<ProvidersApi>, didChangeConnection: Emitter<IntegrationConnectionChangeEvent>, domain: string);
|
|
25
|
+
protected apiBaseUrlFor(session: ProviderAuthenticationSession): string;
|
|
26
|
+
/**
|
|
27
|
+
* The credential AND the installation it is used against: one integration instance serves every installation on
|
|
28
|
+
* its host (context paths differ, the host doesn't), and the same token connected to two of them must not reuse
|
|
29
|
+
* one installation's collections or projects on the other.
|
|
30
|
+
*/
|
|
31
|
+
protected discoveryKey(session: ProviderAuthenticationSession): string;
|
|
32
|
+
protected getRepositoriesApiBaseUrl(session: ProviderAuthenticationSession, repos: ProviderReposInput): string;
|
|
33
|
+
protected getApiOptions(session: ProviderAuthenticationSession, doNotConvertToPat?: boolean): {
|
|
34
|
+
tokenWithInfo: TokenWithInfo<GitSelfManagedHostIntegrationId.AzureDevOpsServer>;
|
|
35
|
+
options: {
|
|
36
|
+
isPAT: boolean;
|
|
37
|
+
baseUrl?: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
protected getCollectionApiOptions(session: ProviderAuthenticationSession, collection: string): {
|
|
41
|
+
isPAT: boolean;
|
|
42
|
+
baseUrl?: string;
|
|
43
|
+
};
|
|
44
|
+
/** The base a request below `collection` appends to: the installation, whichever the address already ends in. */
|
|
45
|
+
protected collectionApiBaseUrl(session: ProviderAuthenticationSession, collection: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Only the server level lists the collections: below one, Azure DevOps Server answers `projectCollections` with a
|
|
48
|
+
* 404 page. On that 404 an address that names a collection reads the collection's own connection data, whose
|
|
49
|
+
* `webApplicationRelativeDirectory` spells it, and reports it as the one collection visible there.
|
|
50
|
+
*/
|
|
51
|
+
protected requestResourcesForUser(session: ProviderAuthenticationSession, userId: string): Promise<ProviderAzureResource[] | undefined>;
|
|
52
|
+
/**
|
|
53
|
+
* The filtered work-item search: one WIQL query per page over ONE collection, then one detail read for the
|
|
54
|
+
* page's slice. See {@link resolveWorkItemSearchScope} for why a search never spans several collections.
|
|
55
|
+
*/
|
|
56
|
+
protected searchProviderIssuesPage(session: ProviderAuthenticationSession, options: {
|
|
57
|
+
repos?: ProviderRepoInput[];
|
|
58
|
+
org?: string;
|
|
59
|
+
criteria?: IssueSearchCriteria;
|
|
60
|
+
cursor?: string;
|
|
61
|
+
pageSize?: number;
|
|
62
|
+
}, cancellation?: AbortSignal): Promise<ProviderIssueSearchPage | undefined>;
|
|
63
|
+
/**
|
|
64
|
+
* Counts each scope with the WIQL its search would page through, so a count never previews constraints the read
|
|
65
|
+
* doesn't apply. A scope this provider refuses (see {@link resolveWorkItemSearchScope}) is answered as that
|
|
66
|
+
* refusal, so the facade drops only it; a request failure still fails the batch, which the facade isolates from
|
|
67
|
+
* the other batches.
|
|
68
|
+
*/
|
|
69
|
+
protected countProviderIssues(session: ProviderAuthenticationSession, scopes: readonly {
|
|
70
|
+
repos?: ProviderRepoInput[];
|
|
71
|
+
org?: string;
|
|
72
|
+
criteria?: IssueSearchCriteria;
|
|
73
|
+
}[], cancellation?: AbortSignal): Promise<ProviderSearchCount[] | undefined>;
|
|
74
|
+
/**
|
|
75
|
+
* The ONE collection a work-item search runs in, and the projects that bound it.
|
|
76
|
+
*
|
|
77
|
+
* A WIQL query is collection-scoped, and ordering a union of several would need every match's details rather
|
|
78
|
+
* than one page's, so a search never spans collections. `org` names the collection; without it the repositories'
|
|
79
|
+
* collection is used, and without either the only collection the account can see. Anything else — repositories
|
|
80
|
+
* in two collections, or several visible collections and nothing to choose one — is refused rather than
|
|
81
|
+
* narrowed to whichever came first.
|
|
82
|
+
*
|
|
83
|
+
* Repositories bound the query by their PROJECT, since Azure work items belong to projects rather than to
|
|
84
|
+
* repositories, and every project must be one the collection actually has: WIQL answers an unknown project name
|
|
85
|
+
* with an empty result, which would read as "nothing matched".
|
|
86
|
+
*/
|
|
87
|
+
private resolveWorkItemSearchScope;
|
|
88
|
+
/**
|
|
89
|
+
* The filtered pull-request search. Azure's pull request query can only narrow by creator, reviewer, status and
|
|
90
|
+
* repository, so every facet is drained (see {@link drainPullRequestSearch}) and the criteria it can't express
|
|
91
|
+
* are applied to the complete facets; the union is then ordered and served from a keyset cursor.
|
|
92
|
+
*/
|
|
93
|
+
protected searchProviderPullRequestsPage(session: ProviderAuthenticationSession, options: {
|
|
94
|
+
repos?: ProviderRepoInput[];
|
|
95
|
+
org?: string;
|
|
96
|
+
criteria?: PullRequestSearchCriteria;
|
|
97
|
+
cursor?: string;
|
|
98
|
+
pageSize?: number;
|
|
99
|
+
summary?: boolean;
|
|
100
|
+
}, cancellation?: AbortSignal): Promise<ProviderPullRequestSearchPage | undefined>;
|
|
101
|
+
/**
|
|
102
|
+
* Counts each scope from the latest drain of its query, the one a search's first page just read, or drains it; see
|
|
103
|
+
* {@link searchProviderPullRequestsPage}.
|
|
104
|
+
*/
|
|
105
|
+
protected countProviderPullRequests(session: ProviderAuthenticationSession, scopes: readonly {
|
|
106
|
+
repos?: ProviderRepoInput[];
|
|
107
|
+
org?: string;
|
|
108
|
+
criteria?: PullRequestSearchCriteria;
|
|
109
|
+
}[], cancellation?: AbortSignal): Promise<(ProviderPullRequestCount | Error)[] | undefined>;
|
|
110
|
+
/**
|
|
111
|
+
* The drain each pagination pages through, one per first page (see {@link searchProviderPullRequestsPage}), so
|
|
112
|
+
* the 50 most recently read paginations are kept for as long as they keep reading.
|
|
113
|
+
*/
|
|
114
|
+
private readonly _pullRequestSearches;
|
|
115
|
+
/**
|
|
116
|
+
* Each query's latest drain, which its count reads for {@link pullRequestCountReuseTtl}. Apart from the
|
|
117
|
+
* paginations' drains, so counting and starting searches never evicts a pagination still being read.
|
|
118
|
+
*/
|
|
119
|
+
private readonly _latestPullRequestSearches;
|
|
120
|
+
/**
|
|
121
|
+
* Keyed by the query itself, not its cursor fingerprint, so two queries whose fingerprints collide can never share
|
|
122
|
+
* a drain; by credential and installation, so two accounts or installations never read each other's results; and
|
|
123
|
+
* by `drainId` for a pagination's own drain, left out for the query's latest one.
|
|
124
|
+
*/
|
|
125
|
+
private pullRequestSearchCacheKey;
|
|
126
|
+
private getPullRequestSearch;
|
|
127
|
+
/**
|
|
128
|
+
* Every pull request matching the search, filtered, deduplicated and ordered.
|
|
129
|
+
*
|
|
130
|
+
* One facet per (repository or project) × relationship: `Author` reads by creator, while `Assignee` and
|
|
131
|
+
* `ReviewRequested` both read by reviewer, since Azure has no assignee distinct from its reviewers. Each facet is
|
|
132
|
+
* drained to {@link azurePullRequestSearchFacetLimit}; a facet that still has more marks the whole result
|
|
133
|
+
* truncated, because its unread rows could sort anywhere in the union. A facet that fails fails the search: a
|
|
134
|
+
* union missing one facet would be served in an order it doesn't have.
|
|
135
|
+
*/
|
|
136
|
+
private drainPullRequestSearch;
|
|
137
|
+
/**
|
|
138
|
+
* The repositories or projects a pull-request search reads, each with its collection and project descriptor.
|
|
139
|
+
*
|
|
140
|
+
* Repositories are read one by one; an `org` is its collection's projects; a search bounded only by the current
|
|
141
|
+
* user reads every project the account can see, the same breadth as the account-wide pull request read.
|
|
142
|
+
*/
|
|
143
|
+
private resolvePullRequestSearchFacets;
|
|
144
|
+
private readonly _collectionUserIds;
|
|
145
|
+
/**
|
|
146
|
+
* The current user's identity id in `collection`, read from the collection's own connection data, which is
|
|
147
|
+
* what Azure's creator and reviewer filters compare against. Cached per credential and collection, since it
|
|
148
|
+
* can't change for either.
|
|
149
|
+
*/
|
|
150
|
+
protected getFilterUserId(session: ProviderAuthenticationSession, collection: string): Promise<string | undefined>;
|
|
151
|
+
/** Every collection the account can see, or a refusal when discovery itself failed. */
|
|
152
|
+
private getSearchCollections;
|
|
153
|
+
/**
|
|
154
|
+
* A collection's projects, refused when discovery was incomplete: a search bounded by an incomplete project set
|
|
155
|
+
* would silently omit the missing projects' matches. `refresh` re-reads them rather than trusting the discovery
|
|
156
|
+
* cache, which lives for the whole connection and so doesn't know a project created since.
|
|
157
|
+
*/
|
|
158
|
+
private getSearchProjects;
|
|
159
|
+
private readonly _projectRefreshes;
|
|
160
|
+
/**
|
|
161
|
+
* Re-reads a collection's projects, sharing one read among the refreshes IN FLIGHT at once — a count batch or a
|
|
162
|
+
* multi-repository drain refreshes the same collection once per scope, and each would otherwise re-read every
|
|
163
|
+
* project page. Nothing outlives the read, so the next search still sees a project created after it.
|
|
164
|
+
*/
|
|
165
|
+
private refreshSearchProjects;
|
|
166
|
+
/**
|
|
167
|
+
* Refuses dates the search can't apply before anything is read. The facade only checks that dates are
|
|
168
|
+
* supported, not their shape, and a refusal here stays with its own scope in a count batch.
|
|
169
|
+
*/
|
|
170
|
+
private assertSearchDates;
|
|
171
|
+
private searchUnavailable;
|
|
172
|
+
protected providerOnDisconnect(): void;
|
|
173
|
+
protected _requestForCurrentUser(session: ProviderAuthenticationSession): Promise<Account | undefined>;
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//# sourceMappingURL=azureDevOpsServer.d.ts.map
|