@gitkraken/core-gitlens 0.16.1 → 0.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/git/watching/classifyChange.js +5 -2
  3. package/dist/git/watching/classifyChange.js.map +1 -1
  4. package/dist/git/watching/watchGroup.js +2 -1
  5. package/dist/git/watching/watchGroup.js.map +1 -1
  6. package/dist/git-cli/providers/branches.d.ts +33 -9
  7. package/dist/git-cli/providers/branches.js +124 -41
  8. package/dist/git-cli/providers/branches.js.map +1 -1
  9. package/dist/plus/integrations/integrationService.d.ts +1 -0
  10. package/dist/plus/integrations/integrationService.js +18 -5
  11. package/dist/plus/integrations/integrationService.js.map +1 -1
  12. package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
  13. package/dist/plus/integrations/providers/apiClients.js +12 -3
  14. package/dist/plus/integrations/providers/apiClients.js.map +1 -1
  15. package/dist/plus/integrations/providers/azure/azure.d.ts +13 -11
  16. package/dist/plus/integrations/providers/azure/azure.js +128 -47
  17. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  18. package/dist/plus/integrations/providers/azure/models.d.ts +4 -10
  19. package/dist/plus/integrations/providers/azure/models.js +25 -9
  20. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  21. package/dist/plus/integrations/providers/azure/workItemDescriptions.d.ts +10 -0
  22. package/dist/plus/integrations/providers/azure/workItemDescriptions.js +48 -0
  23. package/dist/plus/integrations/providers/azure/workItemDescriptions.js.map +1 -0
  24. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +20 -19
  25. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +65 -40
  26. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  27. package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
  28. package/dist/plus/integrations/providers/providerApiClient.d.ts +9 -0
  29. package/dist/plus/integrations/providers/providerApiClient.js +66 -0
  30. package/dist/plus/integrations/providers/providerApiClient.js.map +1 -0
  31. package/dist/plus/integrations/providers/providersApi.d.ts +10 -9
  32. package/dist/plus/integrations/providers/providersApi.js +163 -145
  33. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  34. package/dist/plus/integrations/reads/drains.d.ts +0 -1
  35. package/docs/integrations.md +30 -13
  36. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"azure.js","names":["Logger"],"sources":["../../../../../../plus/integrations/src/providers/azure/azure.ts"],"sourcesContent":["import type { AzureWorkItemHtmlField, AzureWorkItemTypeStates } from '@gitkraken/provider-apis';\nimport { needsAzureWorkItemHtmlFields } from '@gitkraken/provider-apis';\nimport * as l10n from '@vscode/l10n';\nimport type { UnidentifiedAuthor } from '@gitlens/git/models/author.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { Issue, IssueSearchCriteria } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestType } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequest } from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport { effectiveIssueSort } from '@gitlens/git/utils/issue.utils.js';\nimport { base64 } from '@gitlens/utils/base64.js';\nimport { CancellationError, isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { sha256, uuid } from '@gitlens/utils/crypto.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport type { Disposable } from '@gitlens/utils/disposable.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { mapBounded } from '@gitlens/utils/promise.js';\nimport type { CacheController } from '@gitlens/utils/promiseCache.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport type { TokenWithInfo } from '../../authentication/models.js';\nimport type { IntegrationServiceContext } from '../../context.js';\nimport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tisRateLimitResponse,\n\tProviderFetchError,\n\tRequestClientError,\n\tRequestNotFoundError,\n\ttoRateLimitError,\n} from '../../errors.js';\nimport type { ProviderIssueSearchPage } from '../../models/issueReads.js';\nimport { decodePathSegment } from '../../utils/domain.utils.js';\nimport type { ProviderApiConfig } from '../apiConfig.js';\nimport { baseProviderApiConfig } from '../apiConfig.js';\nimport type {\n\tAzureForkRepositoryUrls,\n\tAzureGitCommit,\n\tAzureProjectDescriptor,\n\tAzurePullRequest,\n\tAzurePullRequestWithLinks,\n\tAzureRepositoryReference,\n\tAzureRepositoryUrls,\n\tAzureRepositoryWithMetadata,\n\tAzureWorkItemStateCategory,\n\tWorkItem,\n} from './models.js';\nimport {\n\tazurePullRequestStatusToState,\n\tazureWorkItemsStateCategoryToState,\n\tencodeAzurePathSegment,\n\tfromAzurePullRequest,\n\tfromAzureWorkItem,\n\tgetAzurePullRequestWebUrl,\n\tisClosedAzurePullRequestStatus,\n\tnormalizeAzureBranchName,\n\tparseAzureWorkItemDate,\n\tsanitizeAzureRepositoryUrl,\n} from './models.js';\nimport type { AzureWorkItemSearchCursor } from './search.js';\nimport {\n\tazureWorkItemBatchLimit,\n\tazureWorkItemSearchResultLimit,\n\tazureWorkItemWiqlLengthLimit,\n\tparseAzureWorkItemSearchCursor,\n\ttoAzureSearchPageSize,\n\ttoAzureWorkItemSearchCursorKey,\n\ttoAzureWorkItemSearchQueries,\n\ttoAzureWorkItemSearchWiql,\n} from './search.js';\nimport { queryAzureWorkItemSearch } from './workItemSearch.js';\nimport {\n\tAzureWorkItemStatesCache,\n\tgetAzureWorkItemFieldsPath,\n\tgetAzureWorkItemTypeStatesPath,\n\tgetAzureWorkItemTypesUrl,\n} from './workItemStateCategories.js';\n\nconst forkRepositoryUrlCacheTtl = 5 * 60 * 1000;\nconst workItemMetadataConcurrency = 6;\n/** How long a work-item search's id snapshot outlives the last page read from it, and the most it lives at all. */\nconst workItemSearchSnapshotTtl = 5 * 60 * 1000;\nconst workItemSearchSnapshotMaxTtl = 30 * 60 * 1000;\n\ntype WorkItemSearchOptions = {\n\tbaseUrl: string;\n\tcriteria?: IssueSearchCriteria;\n\tprojectNames?: readonly string[];\n\tworkflowProjectNames?: readonly string[];\n};\n\n/** Whether Azure refused a WIQL query for matching more work items than it will return (`VS402337`). */\nfunction isWorkItemLimitRefusal(ex: unknown): boolean {\n\treturn ex instanceof RequestClientError && ex.message.includes('VS402337');\n}\n\nfunction parseAzureRepositoryDescriptor(repo: string): { projectName: string; repoName: string } {\n\tconst parts = repo.split('/');\n\tconst [projectName, segment, repoName] = parts;\n\tif (parts.length !== 3 || segment !== '_git' || !projectName || !repoName) {\n\t\tthrow new Error(`Invalid Azure repository descriptor '${repo}'; expected '{project}/_git/{repoName}'.`);\n\t}\n\n\treturn { projectName: projectName, repoName: repoName };\n}\n\nexport class AzureDevOpsApi implements Disposable {\n\tprivate readonly _disposable: Disposable | undefined;\n\tprivate readonly _workItemStates: AzureWorkItemStatesCache;\n\tprivate readonly ownsWorkItemStates: boolean;\n\tprivate readonly _forkRepositoryUrls = new PromiseCache<string, AzureForkRepositoryUrls | undefined>({\n\t\tcapacity: 100,\n\t\tcreateTTL: forkRepositoryUrlCacheTtl,\n\t});\n\t/**\n\t * The ordered ids each filtered work-item search's first page queried, paged through by its continuations. Kept\n\t * alive while a pagination keeps reading them (see `workItemSearchSnapshotTtl`). Every first page adds one, so\n\t * the capacity leaves room for repeated first pages before an idle pagination's snapshot is the one evicted.\n\t */\n\tprivate readonly _workItemSearches = new PromiseCache<string, number[]>({\n\t\tcapacity: 50,\n\t\taccessTTL: workItemSearchSnapshotTtl,\n\t\tcreateTTL: workItemSearchSnapshotMaxTtl,\n\t});\n\n\tconstructor(\n\t\tprivate readonly config: ProviderApiConfig,\n\t\tworkItemStates?: AzureWorkItemStatesCache,\n\t) {\n\t\tthis._workItemStates = workItemStates ?? new AzureWorkItemStatesCache();\n\t\tthis.ownsWorkItemStates = workItemStates == null;\n\t\tthis._disposable = config.onConfigChanged?.(() => this.resetCaches());\n\t}\n\n\tdispose(): void {\n\t\tthis._disposable?.dispose();\n\t}\n\n\tprivate resetCaches(): void {\n\t\tif (this.ownsWorkItemStates) {\n\t\t\tthis._workItemStates.clear();\n\t\t}\n\t\tthis._forkRepositoryUrls.clear();\n\t\tthis._workItemSearches.clear();\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, branch) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tbranch: branch,\n\t\t}),\n\t})\n\tpublic async getPullRequestForBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tbranch: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\n\t\ttry {\n\t\t\tconst prResult = await this.request<{ value: AzurePullRequest[] }>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/pullRequests?searchCriteria.status=all&searchCriteria.sourceRefName=${encodeURIComponent(`refs/heads/${branch}`)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\t// Sort PRs: open PRs first, then by most recent activity (creation or closure date)\n\t\t\tconst sortedPRs = prResult?.value.sort((a, b) => {\n\t\t\t\t// First, prioritize open PRs (active/notSet) over closed ones (abandoned/completed)\n\t\t\t\tconst aIsOpen = a.status === 'active' || a.status === 'notSet';\n\t\t\t\tconst bIsOpen = b.status === 'active' || b.status === 'notSet';\n\n\t\t\t\tif (aIsOpen !== bIsOpen) {\n\t\t\t\t\treturn aIsOpen ? -1 : 1; // Open PRs come first\n\t\t\t\t}\n\n\t\t\t\t// Among PRs with the same status, sort by most recent activity\n\t\t\t\t// Use closedDate if available, otherwise use creationDate\n\t\t\t\tconst aDate = new Date(a.closedDate || a.creationDate);\n\t\t\t\tconst bDate = new Date(b.closedDate || b.creationDate);\n\n\t\t\t\treturn bDate.getTime() - aDate.getTime(); // Most recent first\n\t\t\t});\n\n\t\t\tconst pr = sortedPRs?.[0];\n\t\t\tif (pr == null) return undefined;\n\n\t\t\treturn await this.toPullRequest(pr, provider, token, owner, options.baseUrl, scope);\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is actionable and must not be reported as an absent result; every other\n\t\t\t// failure keeps the existing degrade-to-undefined behavior.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tscope?.error(ex);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, rev, baseUrl) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\trev: rev,\n\t\t\tbaseUrl: baseUrl,\n\t\t}),\n\t})\n\tasync getPullRequestForCommit(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\trev: string,\n\t\tbaseUrl: string,\n\t\t_options?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\t\ttry {\n\t\t\tconst prResult = await this.request<{ results: Record<string, AzurePullRequest[]>[] }>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/pullrequestquery?api-version=4.1`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tqueries: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\titems: [rev],\n\t\t\t\t\t\t\t\ttype: 'commit',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\n\t\t\tconst pr = prResult?.results[0]?.[rev]?.[0];\n\t\t\tif (pr == null) return undefined;\n\n\t\t\tconst pullRequest = await this.request<AzurePullRequestWithLinks>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(pr.repository.project.id)}/_apis/git/repositories/${encodeAzurePathSegment(pr.repository.id)}/pullRequests/${encodeAzurePathSegment(pr.pullRequestId.toString())}`,\n\t\t\t\t{ method: 'GET' },\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tif (pullRequest == null) return undefined;\n\n\t\t\treturn await this.toPullRequest(pullRequest, provider, token, owner, baseUrl, scope, cancellation);\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is actionable and must not be reported as an absent result; every other\n\t\t\t// failure keeps the existing degrade-to-undefined behavior.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tscope?.error(ex);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, projectOrName, id) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\tprojectOrName: projectOrName,\n\t\t\tid: id,\n\t\t}),\n\t})\n\tpublic async getPullRequest(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tprojectOrName: string,\n\t\tid: string,\n\t\toptions: { baseUrl: string },\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\t// A pull request id is unique within the organization, so no repository is required here — only the\n\t\t// project. `projectOrName` is either a bare project name or a `{project}/_git/{repo}` descriptor; either\n\t\t// way the project is its first segment.\n\t\tconst projectName = projectOrName.split('/')[0];\n\n\t\ttry {\n\t\t\tconst pr = await this.request<AzurePullRequest>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/pullrequests/${encodeAzurePathSegment(id)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\treturn pr != null\n\t\t\t\t? await this.toPullRequest(pr, provider, token, owner, options.baseUrl, scope)\n\t\t\t\t: undefined;\n\t\t} catch (ex) {\n\t\t\t// Only a 404 is a proven absence; `Integration.getPullRequest` handles every other failure, so it is not\n\t\t\t// cached as a miss\n\t\t\tif (ex.original?.status === 404) return undefined;\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, id) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tid: id,\n\t\t}),\n\t})\n\tpublic async getIssueOrPullRequest(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tid: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t\ttype?: IssueOrPullRequestType;\n\t\t},\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\n\t\tif (options?.type === undefined || options?.type === 'issue') {\n\t\t\tlet issueResult: WorkItem | undefined;\n\t\t\ttry {\n\t\t\t\t// Try to get the Work item (wit) first with specific fields\n\t\t\t\tissueResult = await this.request<WorkItem>(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\toptions?.baseUrl,\n\t\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/wit/workItems/${encodeAzurePathSegment(id)}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t},\n\t\t\t\t\tscope,\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\t// A rejected credential is actionable and must not be reported as an absent issue; every other\n\t\t\t\t// non-404 keeps the existing degrade-to-undefined behavior.\n\t\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\t\tif (ex.original?.status !== 404) {\n\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (issueResult != null) {\n\t\t\t\tconst issueType = issueResult.fields['System.WorkItemType'];\n\t\t\t\tconst state = issueResult.fields['System.State'];\n\t\t\t\tconst stateCategory = await this.getWorkItemStateCategory(\n\t\t\t\t\tissueType,\n\t\t\t\t\tstate,\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\towner,\n\t\t\t\t\tprojectName,\n\t\t\t\t\toptions,\n\t\t\t\t);\n\n\t\t\t\tconst normalizedState = azureWorkItemsStateCategoryToState(\n\t\t\t\t\tstateCategory,\n\t\t\t\t\tparseAzureWorkItemDate(issueResult.fields['Microsoft.VSTS.Common.ClosedDate']) ?? undefined,\n\t\t\t\t);\n\n\t\t\t\treturn {\n\t\t\t\t\tid: issueResult.id.toString(),\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnodeId: issueResult.id.toString(),\n\t\t\t\t\tprovider: provider,\n\t\t\t\t\tcreatedDate: new Date(issueResult.fields['System.CreatedDate']),\n\t\t\t\t\tupdatedDate: new Date(issueResult.fields['System.ChangedDate']),\n\t\t\t\t\tstate: normalizedState,\n\t\t\t\t\tclosed: normalizedState === 'closed',\n\t\t\t\t\ttitle: issueResult.fields['System.Title'],\n\t\t\t\t\turl: issueResult._links.html.href,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif (options?.type === undefined || options?.type === 'pullrequest') {\n\t\t\ttry {\n\t\t\t\tconst prResult = await this.request<AzurePullRequestWithLinks>(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\toptions?.baseUrl,\n\t\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/pullRequests/${encodeAzurePathSegment(id)}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t},\n\t\t\t\t\tscope,\n\t\t\t\t);\n\n\t\t\t\tif (prResult != null) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: prResult.pullRequestId.toString(),\n\t\t\t\t\t\ttype: 'pullrequest',\n\t\t\t\t\t\tnodeId: prResult.pullRequestId.toString(), // prResult.artifactId maybe?\n\t\t\t\t\t\tprovider: provider,\n\t\t\t\t\t\tcreatedDate: new Date(prResult.creationDate),\n\t\t\t\t\t\tupdatedDate: new Date(prResult.creationDate),\n\t\t\t\t\t\tstate: azurePullRequestStatusToState(prResult.status),\n\t\t\t\t\t\tclosed: isClosedAzurePullRequestStatus(prResult.status),\n\t\t\t\t\t\ttitle: prResult.title,\n\t\t\t\t\t\turl: getAzurePullRequestWebUrl(prResult, options.baseUrl, owner),\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn undefined;\n\t\t\t} catch (ex) {\n\t\t\t\t// A rejected credential is actionable and must not be reported as an absent issue; every other\n\t\t\t\t// non-404 keeps the existing degrade-to-undefined behavior.\n\t\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\t\tif (ex.original?.status !== 404) {\n\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t@trace({\n\t\targs: (provider, token, project, id) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tproject: project,\n\t\t\tid: id,\n\t\t}),\n\t})\n\tpublic async getIssue(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tproject: AzureProjectDescriptor,\n\t\tid: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t): Promise<Issue | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tlet issueResult: WorkItem | undefined;\n\t\ttry {\n\t\t\t// Try to get the Work item (wit) first with specific fields\n\t\t\tissueResult = await this.request<WorkItem>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`${encodeAzurePathSegment(project.resourceName)}/${encodeAzurePathSegment(project.name)}/_apis/wit/workItems/${encodeAzurePathSegment(id)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is actionable and must not be reported as an absent work item; every other\n\t\t\t// non-404 keeps the existing degrade-to-undefined behavior.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tif (ex.original?.status !== 404) {\n\t\t\t\tscope?.error(ex);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\tif (issueResult != null) {\n\t\t\tconst issueType = issueResult.fields['System.WorkItemType'];\n\t\t\tconst state = issueResult.fields['System.State'];\n\t\t\t// Independent reads, so a cold cache costs one round trip rather than two.\n\t\t\tconst [stateCategory, htmlFields] = await Promise.all([\n\t\t\t\tthis.getWorkItemStateCategory(\n\t\t\t\t\tissueType,\n\t\t\t\t\tstate,\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tproject.resourceName,\n\t\t\t\t\tproject.name,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t\tneedsAzureWorkItemHtmlFields(issueResult.fields)\n\t\t\t\t\t? this.getWorkItemHtmlFields(provider, token, options.baseUrl, project.resourceName, scope)\n\t\t\t\t\t: undefined,\n\t\t\t]);\n\t\t\treturn fromAzureWorkItem(issueResult, provider, project, stateCategory, 'point', htmlFields);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, rev, baseUrl) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\trev: rev,\n\t\t\tbaseUrl: baseUrl,\n\t\t}),\n\t})\n\tasync getAccountForCommit(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\trev: string,\n\t\tbaseUrl: string,\n\t\t_options?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<UnidentifiedAuthor | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\n\t\ttry {\n\t\t\t// Try to get the Work item (wit) first with specific fields\n\t\t\tconst commit = await this.request<AzureGitCommit>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/commits/${encodeAzurePathSegment(rev)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tconst author = commit?.author;\n\t\t\tif (!author) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\t// Azure API never gives us an id/username we can use, therefore we always return UnidentifiedAuthor\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tid: undefined,\n\t\t\t\tusername: undefined,\n\t\t\t\tname: author?.name,\n\t\t\t\temail: author?.email,\n\t\t\t\tavatarUrl: undefined,\n\t\t\t} satisfies UnidentifiedAuthor;\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is actionable and must not be reported as an absent work item; every other\n\t\t\t// non-404 keeps the existing degrade-to-undefined behavior.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tif (ex.original?.status !== 404) {\n\t\t\t\tscope?.error(ex);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * The collection an Azure DevOps Server address names, or `undefined` when it names the installation.\n\t *\n\t * Below a collection, `connectionData` describes that collection: its `instanceId` is the collection's id, and its\n\t * `webApplicationRelativeDirectory` ends in the collection's path segment (`DefaultCollection/`); it is empty or\n\t * absent at the server level. As measured against Azure DevOps Server 2020. The directory may also carry the\n\t * installation's own virtual directory before the collection (`tfs/DefaultCollection/`), so only its LAST segment\n\t * names the collection, and only when it is the last segment of the address asked about.\n\t */\n\t@trace({\n\t\targs: (provider, token, baseUrl) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tbaseUrl: baseUrl,\n\t\t}),\n\t})\n\tasync getAddressedCollection(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string,\n\t): Promise<{ id: string; name: string } | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst connectionData = await this.request<{ instanceId?: string; webApplicationRelativeDirectory?: string }>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\t'_apis/connectionData',\n\t\t\t{ method: 'GET' },\n\t\t\tscope,\n\t\t);\n\t\tconst segment = connectionData?.webApplicationRelativeDirectory?.split('/').findLast(Boolean);\n\t\tconst addressed = new URL(baseUrl).pathname.split('/').findLast(Boolean);\n\t\tif (segment == null || addressed == null || connectionData?.instanceId == null) return undefined;\n\n\t\tconst name = decodePathSegment(segment);\n\t\tif (name.toLowerCase() !== decodePathSegment(addressed).toLowerCase()) return undefined;\n\n\t\treturn { id: connectionData.instanceId, name: name };\n\t}\n\n\t@trace({\n\t\targs: (provider, token, baseUrl) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tbaseUrl: baseUrl,\n\t\t}),\n\t})\n\tasync getCurrentUserOnServer(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string,\n\t): Promise<{ id: string; name?: string; email?: string; username?: string; avatarUrl?: string } | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst connectionData = await this.request<{\n\t\t\t\tauthenticatedUser?: {\n\t\t\t\t\tid: string;\n\t\t\t\t\tdescriptor: string;\n\t\t\t\t\tisActive: boolean;\n\t\t\t\t\tmetTypeId: number;\n\t\t\t\t\tproviderDisplayName?: string;\n\t\t\t\t\temailAddress?: string;\n\t\t\t\t\tresourceVersion: 2;\n\t\t\t\t\tsubjectDescriptor: string;\n\t\t\t\t\tproperties?: {\n\t\t\t\t\t\tAccount?: {\n\t\t\t\t\t\t\t$type: string;\n\t\t\t\t\t\t\t$value: string;\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\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t'_apis/connectionData',\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tconst user = connectionData?.authenticatedUser;\n\t\t\tconst username = user?.properties?.Account?.$value;\n\t\t\tif (!username) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tid: user.id,\n\t\t\t\tname: user.providerDisplayName,\n\t\t\t\temail: user.emailAddress,\n\t\t\t\tusername: username,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to get current user from ${baseUrl}`);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tasync getWorkItemStateCategory(\n\t\tissueType: string,\n\t\tstate: string,\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tprojectName: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureWorkItemStateCategory | undefined> {\n\t\tconst states = await this._workItemStates.getStates(\n\t\t\t{ baseUrl: options.baseUrl, namespace: owner, project: projectName, token: token.accessToken, isPAT: true },\n\t\t\tissueType,\n\t\t\tsignal =>\n\t\t\t\tthis.request(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\tgetAzureWorkItemTypeStatesPath(owner, projectName, issueType),\n\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\tundefined,\n\t\t\t\t\tsignal,\n\t\t\t\t),\n\t\t\tcancellation,\n\t\t);\n\t\treturn states.find(s => s.name === state)?.category as AzureWorkItemStateCategory | undefined;\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t}),\n\t})\n\tasync getRepositoryMetadata(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst response = await this.getRepository(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\towner,\n\t\t\t\trepo,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tif (response == null) return undefined;\n\n\t\t\t// Azure's `parentRepository` only reliably carries the repo name and its project; a fork's parent\n\t\t\t// lives in the same organization, so the org (`owner`) is the parent owner. Only report `parent`\n\t\t\t// when the parent's name is actually present — never fall back to the fork's own name.\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\towner: owner,\n\t\t\t\t// Prefer the API's canonical name over parsing the composite `repo` descriptor string.\n\t\t\t\tname: response.name,\n\t\t\t\tisFork: response.isFork ?? false,\n\t\t\t\tparent:\n\t\t\t\t\tresponse.isFork && response.parentRepository?.name != null\n\t\t\t\t\t\t? { owner: owner, name: response.parentRepository.name }\n\t\t\t\t\t\t: undefined,\n\t\t\t} satisfies RepositoryMetadata;\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is not a probe outcome: it is actionable, and the caller routes it into the\n\t\t\t// session recovery. A cancellation or a 404 still degrades quietly.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\t// Cancellations and 404s are expected outcomes for a probe; don't log them as errors.\n\t\t\tif (!isCancellationError(ex) && !(ex instanceof RequestNotFoundError)) {\n\t\t\t\tscope?.error(ex);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t}),\n\t})\n\tasync getDefaultBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<DefaultBranch | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst response = await this.getRepository(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\towner,\n\t\t\t\trepo,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tif (response?.defaultBranch == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tname: normalizeAzureBranchName(response.defaultBranch),\n\t\t\t} satisfies DefaultBranch;\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is not a probe outcome: it is actionable, and the caller routes it into the\n\t\t\t// session recovery. A cancellation or a 404 still degrades quietly.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\t// Cancellations and 404s are expected outcomes for a probe; don't log them as errors.\n\t\t\tif (!isCancellationError(ex) && !(ex instanceof RequestNotFoundError)) {\n\t\t\t\tscope?.error(ex);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * One page of a filtered work-item search over a collection: bounded WIQL queries for the ordered ids, then one\n\t * detail read for the page's slice of them.\n\t *\n\t * The ids a first page queried are kept as the snapshot its continuations page through, for as long as the\n\t * pagination keeps reading it, so metadata changes cannot retarget the continuation. Each first page queries its\n\t * own snapshot, so paginations never share one. A continuation whose snapshot is gone is refused (see\n\t * {@link AzureWorkItemSearchCursor}).\n\t *\n\t * The query asks for one id more than {@link azureWorkItemSearchResultLimit}, bounded by `$top`: the extra id is\n\t * what tells \"exactly the limit\" from \"past it\". Past it, the page is `truncated` with `limitReached` and no total,\n\t * since the true count is unknowable here. A server that refuses the match set even so fails the page with a\n\t * \"narrow the search\" error: no bounded query could serve that order's first window.\n\t *\n\t * `resolveProject` maps each result's project name to its descriptor. A work item in a project it doesn't know\n\t * fails the page rather than being dropped or mis-attributed, since either would leave the page disagreeing with\n\t * the count of the same query; the caller refreshes its project discovery before a search so that means a\n\t * project created during the pagination.\n\t */\n\t@trace({\n\t\targs: (provider, token, collection) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tcollection: collection,\n\t\t}),\n\t})\n\tasync searchWorkItemsPage(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\toptions: WorkItemSearchOptions & {\n\t\t\tresolveProject: (name: string) => AzureProjectDescriptor | undefined;\n\t\t\tcursor?: string;\n\t\t\tpageSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<ProviderIssueSearchPage> {\n\t\tconst scope = getScopedLogger();\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst identity = JSON.stringify([\n\t\t\toptions.criteria?.state ?? 'open',\n\t\t\ttoAzureWorkItemSearchWiql(\n\t\t\t\t{ ...options.criteria, state: 'all' },\n\t\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\t\toptions.projectNames,\n\t\t\t),\n\t\t]);\n\t\tconst key = toAzureWorkItemSearchCursorKey(collection, identity);\n\t\tconst pageSize = toAzureSearchPageSize(options.pageSize, 100, azureWorkItemBatchLimit);\n\t\tconst cursor = parseAzureWorkItemSearchCursor(options.cursor, key, pageSize);\n\t\tconst page = cursor?.page ?? 1;\n\n\t\t// Keep the full request identity: metadata changes cannot retarget a live snapshot, and a hash collision\n\t\t// cannot let a different query or credential read it.\n\t\tconst snapshot = cursor?.snapshot ?? uuid();\n\t\tconst snapshotKey = JSON.stringify([\n\t\t\tawait sha256(token.accessToken),\n\t\t\toptions.baseUrl,\n\t\t\tcollection,\n\t\t\tidentity,\n\t\t\tsnapshot,\n\t\t]);\n\t\t// A continuation only ever READS its snapshot: resuming against a fresh query could skip a work item silently.\n\t\tconst snapshotFound = cursor?.snapshot != null ? this._workItemSearches.get(snapshotKey) : undefined;\n\t\tif (cursor?.snapshot != null && snapshotFound == null) {\n\t\t\tthrow new Error('Work item search results expired; restart the read without a cursor');\n\t\t}\n\n\t\tconst result = await (snapshotFound ??\n\t\t\tthis._workItemSearches.getOrCreate(\n\t\t\t\tsnapshotKey,\n\t\t\t\t(_cacheable, signal) =>\n\t\t\t\t\tthis.readWorkItemSearch(provider, token, collection, options, true, scope, signal).catch(\n\t\t\t\t\t\t(ex: unknown) => {\n\t\t\t\t\t\t\tif (isWorkItemLimitRefusal(ex)) {\n\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t`${provider.name} refused a work item search matching more than ${azureWorkItemSearchResultLimit} results; narrow the search`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t{ cancellation: cancellation },\n\t\t\t));\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst limitReached = result.length > azureWorkItemSearchResultLimit;\n\t\tconst ids = limitReached ? result.slice(0, azureWorkItemSearchResultLimit) : result;\n\n\t\tconst offset = cursor?.offset ?? 0;\n\n\t\tconst slice = ids.slice(offset, offset + pageSize);\n\t\tconst values: Issue[] = [];\n\t\tif (slice.length > 0) {\n\t\t\tconst workItems = await this.request<{ value: (WorkItem | null)[] }>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\t`${encodeAzurePathSegment(collection)}/_apis/wit/workitemsbatch?api-version=5.0`,\n\t\t\t\t{ method: 'POST', body: JSON.stringify({ ids: slice, $expand: 'Links', errorPolicy: 'Omit' }) },\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tconst byId = new Map((workItems?.value ?? []).filter(w => w != null).map(w => [w.id, w]));\n\t\t\tconst htmlFields = [...byId.values()].some(w => needsAzureWorkItemHtmlFields(w.fields))\n\t\t\t\t? await this.getWorkItemHtmlFields(provider, token, options.baseUrl, collection, scope, cancellation)\n\t\t\t\t: undefined;\n\t\t\tfor (const id of slice) {\n\t\t\t\t// A work item deleted since the ids were queried is gone, not a failure: skipping it keeps the page\n\t\t\t\t// honest, since it no longer matches anything.\n\t\t\t\tconst workItem = byId.get(id);\n\t\t\t\tif (workItem == null) continue;\n\n\t\t\t\tconst projectName = workItem.fields['System.TeamProject'];\n\t\t\t\tconst project = options.resolveProject(projectName);\n\t\t\t\tif (project == null) {\n\t\t\t\t\tthrow new Error(`Azure DevOps work item ${id} is in project '${projectName}', which isn't visible`);\n\t\t\t\t}\n\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\tconst stateCategory = await this.getWorkItemStateCategory(\n\t\t\t\t\tworkItem.fields['System.WorkItemType'],\n\t\t\t\t\tworkItem.fields['System.State'],\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tcollection,\n\t\t\t\t\tproject.name,\n\t\t\t\t\toptions,\n\t\t\t\t\tcancellation,\n\t\t\t\t);\n\t\t\t\tvalues.push(fromAzureWorkItem(workItem, provider, project, stateCategory, 'search', htmlFields));\n\t\t\t}\n\t\t}\n\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst nextOffset = offset + slice.length;\n\t\tconst hasMore = nextOffset < ids.length;\n\t\tconst next: AzureWorkItemSearchCursor = { key: key, snapshot: snapshot, offset: nextOffset, page: page + 1 };\n\t\treturn {\n\t\t\tvalues: values,\n\t\t\tcursor: hasMore ? JSON.stringify(next) : undefined,\n\t\t\thasMore: hasMore,\n\t\t\tpage: page,\n\t\t\ttruncated: limitReached,\n\t\t\ttotalCount: limitReached ? undefined : ids.length,\n\t\t\tlimitReached: limitReached || undefined,\n\t\t};\n\t}\n\n\t/**\n\t * How many work items the same WIQL {@link searchWorkItemsPage} would page through match, reading no details.\n\t *\n\t * Asks for one id more than the result ceiling, so the count is exact up to it and `'exceeds-limit'` past it —\n\t * never the ceiling itself, which would understate the match set.\n\t */\n\t@trace({\n\t\targs: (provider, token, collection) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tcollection: collection,\n\t\t}),\n\t})\n\tasync countWorkItems(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\toptions: WorkItemSearchOptions,\n\t\tcancellation?: AbortSignal,\n\t): Promise<number | 'exceeds-limit'> {\n\t\tconst scope = getScopedLogger();\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\ttry {\n\t\t\tconst result = await this.readWorkItemSearch(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tcollection,\n\t\t\t\toptions,\n\t\t\t\tfalse,\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\treturn result.length > azureWorkItemSearchResultLimit ? 'exceeds-limit' : result.length;\n\t\t} catch (ex) {\n\t\t\tif (isWorkItemLimitRefusal(ex)) return 'exceeds-limit';\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate async readWorkItemSearch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\toptions: WorkItemSearchOptions,\n\t\tordered: boolean,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<number[]> {\n\t\tconst queries = await this.getWorkItemSearchQueries(provider, token, collection, options, cancellation);\n\t\treturn queryAzureWorkItemSearch(\n\t\t\tqueries,\n\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\tordered,\n\t\t\t(wiql, top, asOf) =>\n\t\t\t\tthis.queryWorkItemIds(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tcollection,\n\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\twiql,\n\t\t\t\t\ttop,\n\t\t\t\t\tscope,\n\t\t\t\t\tcancellation,\n\t\t\t\t\tasOf,\n\t\t\t\t),\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async getWorkItemSearchQueries(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\toptions: WorkItemSearchOptions,\n\t\tcancellation?: AbortSignal,\n\t): Promise<string[]> {\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst projectNames = options.projectNames ?? options.workflowProjectNames ?? [];\n\t\tlet workflows: Map<string, AzureWorkItemTypeStates> | undefined;\n\t\tif (options.criteria?.state !== 'all') {\n\t\t\tconst types = (\n\t\t\t\tawait mapBounded(projectNames, workItemMetadataConcurrency, async project => {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\tconst scope = {\n\t\t\t\t\t\tbaseUrl: options.baseUrl,\n\t\t\t\t\t\tnamespace: collection,\n\t\t\t\t\t\tproject: project,\n\t\t\t\t\t\ttoken: token.accessToken,\n\t\t\t\t\t\tisPAT: true,\n\t\t\t\t\t};\n\t\t\t\t\tconst names = await this._workItemStates.getTypes(\n\t\t\t\t\t\tscope,\n\t\t\t\t\t\tsignal =>\n\t\t\t\t\t\t\tthis.request(\n\t\t\t\t\t\t\t\tprovider,\n\t\t\t\t\t\t\t\ttoken,\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tgetAzureWorkItemTypesUrl(options.baseUrl, collection, project),\n\t\t\t\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tcancellation,\n\t\t\t\t\t);\n\t\t\t\t\treturn names.map(name => ({ project: project, name: name, scope: scope }));\n\t\t\t\t})\n\t\t\t).flat();\n\t\t\tconst states = await mapBounded(types, workItemMetadataConcurrency, async type => ({\n\t\t\t\tname: type.name,\n\t\t\t\tproject: type.project,\n\t\t\t\tstates: await this._workItemStates.getStates(\n\t\t\t\t\ttype.scope,\n\t\t\t\t\ttype.name,\n\t\t\t\t\tsignal =>\n\t\t\t\t\t\tthis.request(\n\t\t\t\t\t\t\tprovider,\n\t\t\t\t\t\t\ttoken,\n\t\t\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\t\t\tgetAzureWorkItemTypeStatesPath(collection, type.project, type.name),\n\t\t\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t),\n\t\t\t\t\tcancellation,\n\t\t\t\t),\n\t\t\t}));\n\t\t\tworkflows = new Map(\n\t\t\t\tprojectNames.map(project => [project, { types: states.filter(type => type.project === project) }]),\n\t\t\t);\n\t\t}\n\n\t\treturn toAzureWorkItemSearchQueries(\n\t\t\toptions.criteria,\n\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\tprojectNames,\n\t\t\tworkflows,\n\t\t);\n\t}\n\n\t/**\n\t * The ids a collection-level WIQL query matches, in its order.\n\t *\n\t * `api-version=5.0` is the oldest version every Azure DevOps Server release this search supports (2019 and later)\n\t * answers; `timePrecision=true` is what lets the criteria's dates compare as instants — see `toWiqlDate`.\n\t */\n\tprivate async queryWorkItemIds(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\tbaseUrl: string,\n\t\twiql: string,\n\t\ttop: number,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t\tasOf?: string,\n\t): Promise<{ ids: number[]; asOf?: string }> {\n\t\tconst query = asOf == null ? wiql : `${wiql} ASOF '${asOf}'`;\n\t\tif (query.length > azureWorkItemWiqlLengthLimit) {\n\t\t\tthrow new Error('Azure DevOps work item search query is too long; narrow the search');\n\t\t}\n\n\t\tconst result = await this.request<{ workItems?: { id: number }[]; asOf?: string }>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\t`${encodeAzurePathSegment(collection)}/_apis/wit/wiql?$top=${top}&timePrecision=true&api-version=5.0`,\n\t\t\t{ method: 'POST', body: JSON.stringify({ query: query }) },\n\t\t\tscope,\n\t\t\tcancellation,\n\t\t);\n\t\tif (result?.workItems == null) throw new Error('Azure DevOps returned no work item query result');\n\n\t\treturn { ids: result.workItems.map(w => w.id), asOf: result.asOf };\n\t}\n\n\tprivate getRepository(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tbaseUrl: string,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureRepositoryWithMetadata | undefined> {\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\t\treturn this.request<AzureRepositoryWithMetadata>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}?api-version=7.1`,\n\t\t\t{ method: 'GET' },\n\t\t\tscope,\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async toPullRequest(\n\t\tpr: AzurePullRequest,\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tbaseUrl: string,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest> {\n\t\tconst forkRepositoryUrls = await this.getForkRepositoryUrls(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\towner,\n\t\t\tbaseUrl,\n\t\t\tpr.forkSource?.repository,\n\t\t\tscope,\n\t\t\tcancellation,\n\t\t);\n\t\t// Only the point reads convert through here.\n\t\treturn fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls, 'point');\n\t}\n\n\t/**\n\t * The organization's rich text fields a work item's description may come from, for a work item none of the\n\t * built-in description fields describes. Best-effort: a failed read leaves the description to those fields rather\n\t * than failing the work item, so it is `undefined` on any failure but a cancellation. Logs to the caller's `scope`,\n\t * captured before its first `await`: a scope read here, after the caller's reads, can be stale in a browser.\n\t */\n\tprivate async getWorkItemHtmlFields(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string,\n\t\torganization: string,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureWorkItemHtmlField[] | undefined> {\n\t\ttry {\n\t\t\treturn await this._workItemStates.getHtmlFields(\n\t\t\t\t{ baseUrl: baseUrl, namespace: organization, token: token.accessToken, isPAT: true },\n\t\t\t\tsignal =>\n\t\t\t\t\tthis.request(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\ttoken,\n\t\t\t\t\t\tbaseUrl,\n\t\t\t\t\t\tgetAzureWorkItemFieldsPath(organization),\n\t\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t),\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\t// Only the caller's own cancellation fails the read: an abort it didn't ask for is just another failure.\n\t\t\tif (isCancellationError(ex) && cancellation?.aborted) throw ex;\n\n\t\t\tscope?.warn(`Unable to read the Azure DevOps field catalog of '${organization}': ${ex}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Resolves the web and HTTPS clone URLs of the fork a cross-repository pull request comes from. Every other\n\t * repository URL the model reports is rebuilt from the pull request payload; only a fork reference lacks the\n\t * project to do that, so only a fork costs a request.\n\t *\n\t * Best-effort by contract: a fork in a project the token cannot read, a deleted fork, or a throttled request\n\t * leaves the head ref without a URL. It must never cost the pull request itself, which is what makes swallowing\n\t * the failure here the right call rather than a shortcut.\n\t */\n\tprivate async getForkRepositoryUrls(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tbaseUrl: string,\n\t\trepository: AzureRepositoryReference | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureForkRepositoryUrls | undefined> {\n\t\tif (repository == null) return undefined;\n\n\t\tlet tokenHash: string;\n\t\ttry {\n\t\t\ttokenHash = await sha256(token.accessToken);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst cacheKey = JSON.stringify([tokenHash, baseUrl, owner, repository.id]);\n\t\t// The request is shared, so the factory is handed the cache's aggregate signal rather than this caller's: it\n\t\t// aborts only once every caller waiting on the entry has cancelled.\n\t\treturn this._forkRepositoryUrls.getOrCreate(\n\t\t\tcacheKey,\n\t\t\t(cacheable, aggregate) =>\n\t\t\t\tthis.fetchForkRepositoryUrls(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\towner,\n\t\t\t\t\tbaseUrl,\n\t\t\t\t\trepository.id,\n\t\t\t\t\tcacheable,\n\t\t\t\t\tscope,\n\t\t\t\t\taggregate,\n\t\t\t\t),\n\t\t\t{ cancellation: cancellation },\n\t\t);\n\t}\n\n\tprivate async fetchForkRepositoryUrls(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tbaseUrl: string,\n\t\trepositoryId: string,\n\t\tcacheable: CacheController,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureForkRepositoryUrls | undefined> {\n\t\ttry {\n\t\t\tconst response = await this.request<AzureRepositoryUrls>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/_apis/git/repositories/${encodeAzurePathSegment(repositoryId)}?api-version=4.1`,\n\t\t\t\t{ method: 'GET' },\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tconst webUrl = sanitizeAzureRepositoryUrl(response?.webUrl, baseUrl, owner);\n\t\t\tconst cloneHttps = sanitizeAzureRepositoryUrl(response?.remoteUrl, baseUrl, owner);\n\n\t\t\t// The clone URL stands in for the web URL when the response omits it — an older `api-version` promises\n\t\t\t// neither field, and naming the fork by the URL git would use beats naming it not at all.\n\t\t\tconst url = webUrl ?? cloneHttps;\n\t\t\tif (url == null) {\n\t\t\t\tcacheable.invalidate();\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\treturn { url: url, cloneHttps: cloneHttps };\n\t\t} catch (ex) {\n\t\t\t// Missing or inaccessible forks are cached briefly; transient failures retry on the next read.\n\t\t\tif (!(ex instanceof RequestNotFoundError) && !(ex instanceof AuthenticationError)) {\n\t\t\t\tcacheable.invalidate();\n\t\t\t}\n\n\t\t\tconst status = ex instanceof ProviderFetchError ? ` (${ex.status})` : '';\n\t\t\tscope?.warn(`Unable to resolve the fork repository URLs${status}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async request<T>(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string | undefined,\n\t\troute: string,\n\t\toptions: { method: RequestInit['method'] } & Record<string, unknown>,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal | undefined,\n\t): Promise<T | undefined> {\n\t\tconst { accessToken } = token;\n\t\tconst url = baseUrl ? `${baseUrl}/${route}` : route;\n\n\t\tlet rsp: Response;\n\t\ttry {\n\t\t\tconst sw = maybeStopWatch(`[AZURE] ${options?.method ?? 'GET'} request`, { log: { onlyExit: true } });\n\n\t\t\ttry {\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\trsp = await this.config.wrapForForcedInsecureSSL(provider.getIgnoreSSLErrors(), () =>\n\t\t\t\t\tthis.config.fetch(url, {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAuthorization: `Basic ${base64(`PAT:${accessToken}`)}`,\n\t\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal: cancellation,\n\t\t\t\t\t\t...options,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\tif (rsp.ok) {\n\t\t\t\t\t// Azure answers a rejected credential by redirecting to its sign-in page, which returns `203\n\t\t\t\t\t// text/html` rather than `401`. `rsp.ok` spans 200-299, so that page used to reach `json()` and\n\t\t\t\t\t// die as a bare `SyntaxError` — which `getIssue` and friends catch and report as \"not found\",\n\t\t\t\t\t// making an invalid credential indistinguishable from a missing work item (GKDEV-3617).\n\t\t\t\t\tconst contentType = rsp.headers.get('content-type')?.toLowerCase() ?? '';\n\t\t\t\t\tif (contentType.startsWith('text/html')) {\n\t\t\t\t\t\tconst { accessToken: _accessToken, ...tokenInfo } = token;\n\t\t\t\t\t\tthrow new AuthenticationError(\n\t\t\t\t\t\t\ttokenInfo,\n\t\t\t\t\t\t\tAuthenticationErrorReason.Unauthorized,\n\t\t\t\t\t\t\tnew Error(`(${rsp.status}) Azure DevOps returned a sign-in page instead of data`),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn (await rsp.json()) as T;\n\t\t\t\t}\n\n\t\t\t\t// Reads the body so the 403 branch below can tell a throttled request from a permission failure.\n\t\t\t\tthrow await ProviderFetchError.fromResponse('AzureDevOps', rsp);\n\t\t\t} finally {\n\t\t\t\tsw?.stop();\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof ProviderFetchError || ex.name === 'AbortError') {\n\t\t\t\tthis.handleRequestError(provider, token, ex, scope);\n\t\t\t} else if (Logger.isDebugging) {\n\t\t\t\tthis.config.onError?.(`AzureDevOps request failed: ${ex.message}`);\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate handleRequestError(\n\t\tprovider: Provider | undefined,\n\t\ttoken: TokenWithInfo,\n\t\tex: ProviderFetchError | (Error & { name: 'AbortError' }),\n\t\tscope: ScopedLogger | undefined,\n\t): void {\n\t\tif (ex.name === 'AbortError' || !(ex instanceof ProviderFetchError)) throw new CancellationError(ex);\n\n\t\tconst { accessToken, ...tokenInfo } = token;\n\t\tswitch (ex.status) {\n\t\t\tcase 404: // Not found\n\t\t\tcase 410: // Gone\n\t\t\tcase 422: // Unprocessable Entity\n\t\t\t\tthrow new RequestNotFoundError(ex);\n\t\t\tcase 401: // Unauthorized\n\t\t\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Unauthorized, ex);\n\t\t\tcase 429: // Too Many Requests\n\t\t\t\tthrow toRateLimitError(ex, accessToken);\n\t\t\tcase 403: // Forbidden\n\t\t\t\t// Azure returns 403 for both a permission failure and a throttled request (\"Request was blocked\n\t\t\t\t// due to exceeding usage of resource 'RateLimit'\"), so the message is the discriminant. Reporting\n\t\t\t\t// a throttle as `auth` would prompt the user to re-authenticate a healthy connection instead of\n\t\t\t\t// retrying (see `isRateLimitResponse`).\n\t\t\t\tif (isRateLimitResponse(ex)) throw toRateLimitError(ex, accessToken);\n\n\t\t\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Forbidden, ex);\n\t\t\tcase 500: // Internal Server Error\n\t\t\t\tscope?.error(ex);\n\t\t\t\tif (ex.response != null) {\n\t\t\t\t\tthis.config.onRequestFailed?.(\n\t\t\t\t\t\tprovider == null || provider.id === 'azure'\n\t\t\t\t\t\t\t? l10n.t(\n\t\t\t\t\t\t\t\t\t'{0} failed to respond and might be experiencing issues. Please visit the [AzureDevOps status page](https://status.dev.azure.com) for more information.',\n\t\t\t\t\t\t\t\t\tprovider?.name ?? 'AzureDevOps',\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: l10n.t('{0} failed to respond and might be experiencing issues.', provider.name),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 502: // Bad Gateway\n\t\t\t\tscope?.error(ex);\n\t\t\t\t// TODO: Learn the Azure API docs and put it in order:\n\t\t\t\t// if (ex.message.includes('timeout')) {\n\t\t\t\t// \tprovider?.trackRequestException();\n\t\t\t\t// \tvoid showIntegrationRequestTimedOutWarningMessage(provider?.name ?? 'Azure');\n\t\t\t\t// \treturn;\n\t\t\t\t// }\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (ex.status >= 400 && ex.status < 500) throw new RequestClientError(ex);\n\t\t\t\tbreak;\n\t\t}\n\n\t\tscope?.error(ex);\n\t\tif (Logger.isDebugging) {\n\t\t\tthis.config.onError?.(\n\t\t\t\t`AzureDevOps request failed: ${(ex.response as any)?.errors?.[0]?.message ?? ex.message}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** Wires an {@link AzureDevOpsApi} from the full runtime context, mapping `ctx` down to the narrow config. */\nexport function createAzureDevOpsApi(\n\tctx: IntegrationServiceContext,\n\tworkItemStates?: AzureWorkItemStatesCache,\n): AzureDevOpsApi {\n\treturn new AzureDevOpsApi(baseProviderApiConfig(ctx), workItemStates);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"azure.js","names":["Logger"],"sources":["../../../../../../plus/integrations/src/providers/azure/azure.ts"],"sourcesContent":["import type {\n\tAzureDevOpsRestResource,\n\tAzureDevOpsRestResourceInfo,\n\tAzureWorkItemHtmlField,\n\tAzureWorkItemTypeStates,\n} from '@gitkraken/provider-apis';\nimport * as l10n from '@vscode/l10n';\nimport type { UnidentifiedAuthor } from '@gitlens/git/models/author.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { Issue, IssueSearchCriteria } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestType } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequest } from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport { effectiveIssueSort } from '@gitlens/git/utils/issue.utils.js';\nimport { base64 } from '@gitlens/utils/base64.js';\nimport { CancellationError, isCancellationError, raceWithSignal } from '@gitlens/utils/cancellation.js';\nimport { sha256, uuid } from '@gitlens/utils/crypto.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport type { Disposable } from '@gitlens/utils/disposable.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { mapBounded } from '@gitlens/utils/promise.js';\nimport type { CacheController } from '@gitlens/utils/promiseCache.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport type { TokenWithInfo } from '../../authentication/models.js';\nimport type { IntegrationServiceContext } from '../../context.js';\nimport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tisRateLimitResponse,\n\tProviderFetchError,\n\tRequestClientError,\n\tRequestNotFoundError,\n\ttoRateLimitError,\n} from '../../errors.js';\nimport type { ProviderIssueSearchPage } from '../../models/issueReads.js';\nimport { decodePathSegment } from '../../utils/domain.utils.js';\nimport type { ProviderApiConfig } from '../apiConfig.js';\nimport { baseProviderApiConfig } from '../apiConfig.js';\nimport { createProviderApiRequest, createProviderApis } from '../providerApiClient.js';\nimport { throwProviderError } from '../providerErrors.js';\nimport type {\n\tAzureForkRepositoryUrls,\n\tAzureGitCommit,\n\tAzureProjectDescriptor,\n\tAzurePullRequest,\n\tAzurePullRequestWithLinks,\n\tAzureRepositoryReference,\n\tAzureRepositoryUrls,\n\tAzureRepositoryWithMetadata,\n\tAzureWorkItemStateCategory,\n\tWorkItem,\n} from './models.js';\nimport {\n\tazurePullRequestStatusToState,\n\tazureWorkItemsStateCategoryToState,\n\tencodeAzurePathSegment,\n\tfromAzurePullRequest,\n\tfromAzureWorkItem,\n\tgetAzurePullRequestWebUrl,\n\tisClosedAzurePullRequestStatus,\n\tnormalizeAzureBranchName,\n\tparseAzureWorkItemDate,\n\tsanitizeAzureRepositoryUrl,\n} from './models.js';\nimport type { AzureWorkItemSearchCursor } from './search.js';\nimport {\n\tazureWorkItemBatchLimit,\n\tazureWorkItemSearchResultLimit,\n\tazureWorkItemWiqlLengthLimit,\n\tparseAzureWorkItemSearchCursor,\n\ttoAzureSearchPageSize,\n\ttoAzureWorkItemSearchCursorKey,\n\ttoAzureWorkItemSearchQueries,\n\ttoAzureWorkItemSearchWiql,\n} from './search.js';\nimport { AzureWorkItemDescriptionsCache } from './workItemDescriptions.js';\nimport { queryAzureWorkItemSearch } from './workItemSearch.js';\nimport {\n\tAzureWorkItemStatesCache,\n\tgetAzureWorkItemTypeStatesPath,\n\tgetAzureWorkItemTypesUrl,\n\trequireAzureWorkItemStateCategories,\n\tresolveAzureWorkItemStateCategory,\n} from './workItemStateCategories.js';\n\nconst forkRepositoryUrlCacheTtl = 5 * 60 * 1000;\nconst workItemMetadataConcurrency = 6;\n/** How long a work-item search's id snapshot outlives the last page read from it, and the most it lives at all. */\nconst workItemSearchSnapshotTtl = 5 * 60 * 1000;\nconst workItemSearchSnapshotMaxTtl = 30 * 60 * 1000;\n\ntype WorkItemSearchOptions = {\n\tbaseUrl: string;\n\tcriteria?: IssueSearchCriteria;\n\tprojectNames?: readonly string[];\n\tworkflowProjectNames?: readonly string[];\n};\n\n/** Whether Azure refused a WIQL query for matching more work items than it will return (`VS402337`). */\nfunction isWorkItemLimitRefusal(ex: unknown): boolean {\n\treturn ex instanceof RequestClientError && ex.message.includes('VS402337');\n}\n\nfunction parseAzureRepositoryDescriptor(repo: string): { projectName: string; repoName: string } {\n\tconst parts = repo.split('/');\n\tconst [projectName, segment, repoName] = parts;\n\tif (parts.length !== 3 || segment !== '_git' || !projectName || !repoName) {\n\t\tthrow new Error(`Invalid Azure repository descriptor '${repo}'; expected '{project}/_git/{repoName}'.`);\n\t}\n\n\treturn { projectName: projectName, repoName: repoName };\n}\n\nexport class AzureDevOpsApi implements Disposable {\n\tprivate readonly restApi: ReturnType<typeof createProviderApis>['azureDevOps'];\n\tprivate readonly _disposable: Disposable | undefined;\n\tprivate readonly _workItemStates: AzureWorkItemStatesCache;\n\tprivate readonly ownsWorkItemStates: boolean;\n\tprivate readonly _workItemDescriptions: AzureWorkItemDescriptionsCache;\n\tprivate readonly ownsWorkItemDescriptions: boolean;\n\tprivate readonly _forkRepositoryUrls = new PromiseCache<string, AzureForkRepositoryUrls | undefined>({\n\t\tcapacity: 100,\n\t\tcreateTTL: forkRepositoryUrlCacheTtl,\n\t});\n\t/**\n\t * The ordered ids each filtered work-item search's first page queried, paged through by its continuations. Kept\n\t * alive while a pagination keeps reading them (see `workItemSearchSnapshotTtl`). Every first page adds one, so\n\t * the capacity leaves room for repeated first pages before an idle pagination's snapshot is the one evicted.\n\t */\n\tprivate readonly _workItemSearches = new PromiseCache<string, number[]>({\n\t\tcapacity: 50,\n\t\taccessTTL: workItemSearchSnapshotTtl,\n\t\tcreateTTL: workItemSearchSnapshotMaxTtl,\n\t});\n\n\tconstructor(\n\t\tprivate readonly config: ProviderApiConfig,\n\t\tworkItemStates?: AzureWorkItemStatesCache,\n\t\tworkItemDescriptions?: AzureWorkItemDescriptionsCache,\n\t) {\n\t\tthis.restApi = createProviderApis({\n\t\t\trequest: createProviderApiRequest((input, init) => config.fetch(input, init)),\n\t\t}).azureDevOps;\n\t\tthis._workItemStates = workItemStates ?? new AzureWorkItemStatesCache();\n\t\tthis.ownsWorkItemStates = workItemStates == null;\n\t\tthis._workItemDescriptions = workItemDescriptions ?? new AzureWorkItemDescriptionsCache();\n\t\tthis.ownsWorkItemDescriptions = workItemDescriptions == null;\n\t\tthis._disposable = config.onConfigChanged?.(() => this.resetCaches());\n\t}\n\n\tdispose(): void {\n\t\tthis._disposable?.dispose();\n\t}\n\n\tprivate resetCaches(): void {\n\t\tthis.restApi.updateConfig({});\n\t\tif (this.ownsWorkItemStates) {\n\t\t\tthis._workItemStates.clear();\n\t\t}\n\t\tif (this.ownsWorkItemDescriptions) {\n\t\t\tthis._workItemDescriptions.clear();\n\t\t}\n\t\tthis._forkRepositoryUrls.clear();\n\t\tthis._workItemSearches.clear();\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, branch) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tbranch: branch,\n\t\t}),\n\t})\n\tpublic async getPullRequestForBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tbranch: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\n\t\ttry {\n\t\t\tconst prResult = await this.request<{ value: AzurePullRequest[] }>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/pullRequests?searchCriteria.status=all&searchCriteria.sourceRefName=${encodeURIComponent(`refs/heads/${branch}`)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\t// Sort PRs: open PRs first, then by most recent activity (creation or closure date)\n\t\t\tconst sortedPRs = prResult?.value.sort((a, b) => {\n\t\t\t\t// First, prioritize open PRs (active/notSet) over closed ones (abandoned/completed)\n\t\t\t\tconst aIsOpen = a.status === 'active' || a.status === 'notSet';\n\t\t\t\tconst bIsOpen = b.status === 'active' || b.status === 'notSet';\n\n\t\t\t\tif (aIsOpen !== bIsOpen) {\n\t\t\t\t\treturn aIsOpen ? -1 : 1; // Open PRs come first\n\t\t\t\t}\n\n\t\t\t\t// Among PRs with the same status, sort by most recent activity\n\t\t\t\t// Use closedDate if available, otherwise use creationDate\n\t\t\t\tconst aDate = new Date(a.closedDate || a.creationDate);\n\t\t\t\tconst bDate = new Date(b.closedDate || b.creationDate);\n\n\t\t\t\treturn bDate.getTime() - aDate.getTime(); // Most recent first\n\t\t\t});\n\n\t\t\tconst pr = sortedPRs?.[0];\n\t\t\tif (pr == null) return undefined;\n\n\t\t\treturn await this.toPullRequest(pr, provider, token, owner, options.baseUrl, scope);\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is actionable and must not be reported as an absent result; every other\n\t\t\t// failure keeps the existing degrade-to-undefined behavior.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tscope?.error(ex);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, rev, baseUrl) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\trev: rev,\n\t\t\tbaseUrl: baseUrl,\n\t\t}),\n\t})\n\tasync getPullRequestForCommit(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\trev: string,\n\t\tbaseUrl: string,\n\t\t_options?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\t\tconst resource = await this.requireRestResource(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\towner,\n\t\t\tbaseUrl,\n\t\t\t'pullRequestQuery',\n\t\t\t'4.1',\n\t\t\tcancellation,\n\t\t);\n\t\ttry {\n\t\t\tconst encodedProject = encodeAzurePathSegment(projectName);\n\t\t\tconst encodedRepo = encodeAzurePathSegment(repoName);\n\t\t\tconst prResult = await this.request<{ results: Record<string, AzurePullRequest[]>[] }>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodedProject}/_apis/git/repositories/${encodedRepo}/pullrequestquery?api-version=${resource.apiVersion}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tqueries: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\titems: [rev],\n\t\t\t\t\t\t\t\ttype: 'commit',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\n\t\t\tconst pr = prResult?.results[0]?.[rev]?.[0];\n\t\t\tif (pr == null) return undefined;\n\n\t\t\tconst pullRequest = await this.request<AzurePullRequestWithLinks>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(pr.repository.project.id)}/_apis/git/repositories/${encodeAzurePathSegment(pr.repository.id)}/pullRequests/${encodeAzurePathSegment(pr.pullRequestId.toString())}`,\n\t\t\t\t{ method: 'GET' },\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tif (pullRequest == null) return undefined;\n\n\t\t\treturn await this.toPullRequest(pullRequest, provider, token, owner, baseUrl, scope, cancellation);\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is actionable and must not be reported as an absent result; every other\n\t\t\t// failure keeps the existing degrade-to-undefined behavior.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tscope?.error(ex);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, projectOrName, id) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\tprojectOrName: projectOrName,\n\t\t\tid: id,\n\t\t}),\n\t})\n\tpublic async getPullRequest(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tprojectOrName: string,\n\t\tid: string,\n\t\toptions: { baseUrl: string },\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\t// A pull request id is unique within the organization, so no repository is required here — only the\n\t\t// project. `projectOrName` is either a bare project name or a `{project}/_git/{repo}` descriptor; either\n\t\t// way the project is its first segment.\n\t\tconst projectName = projectOrName.split('/')[0];\n\n\t\ttry {\n\t\t\tconst pr = await this.request<AzurePullRequest>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/pullrequests/${encodeAzurePathSegment(id)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\treturn pr != null\n\t\t\t\t? await this.toPullRequest(pr, provider, token, owner, options.baseUrl, scope)\n\t\t\t\t: undefined;\n\t\t} catch (ex) {\n\t\t\t// Only a 404 is a proven absence; `Integration.getPullRequest` handles every other failure, so it is not\n\t\t\t// cached as a miss\n\t\t\tif (ex.original?.status === 404) return undefined;\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, id) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tid: id,\n\t\t}),\n\t})\n\tpublic async getIssueOrPullRequest(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tid: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t\ttype?: IssueOrPullRequestType;\n\t\t},\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\n\t\tif (options?.type === undefined || options?.type === 'issue') {\n\t\t\tconst resource = await this.requireRestResource(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\towner,\n\t\t\t\toptions.baseUrl,\n\t\t\t\t'workItems',\n\t\t\t\t'6.0',\n\t\t\t);\n\t\t\tlet issueResult: WorkItem | undefined;\n\t\t\ttry {\n\t\t\t\tissueResult = await this.getWorkItem(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\towner,\n\t\t\t\t\tprojectName,\n\t\t\t\t\tid,\n\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\tresource,\n\t\t\t\t\tscope,\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\t// A rejected credential is actionable and must not be reported as an absent issue; every other\n\t\t\t\t// non-404 keeps the existing degrade-to-undefined behavior.\n\t\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\t\tif (ex.original?.status !== 404) {\n\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (issueResult != null) {\n\t\t\t\tawait this.requireWorkItemProject(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\towner,\n\t\t\t\t\tprojectName,\n\t\t\t\t\tissueResult,\n\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\tscope,\n\t\t\t\t);\n\t\t\t\tconst issueType = issueResult.fields['System.WorkItemType'];\n\t\t\t\tconst state = issueResult.fields['System.State'];\n\t\t\t\tconst stateCategory = await this.getWorkItemStateCategory(\n\t\t\t\t\tissueType,\n\t\t\t\t\tstate,\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\towner,\n\t\t\t\t\tprojectName,\n\t\t\t\t\toptions,\n\t\t\t\t);\n\n\t\t\t\tconst normalizedState = azureWorkItemsStateCategoryToState(\n\t\t\t\t\tstateCategory,\n\t\t\t\t\tparseAzureWorkItemDate(issueResult.fields['Microsoft.VSTS.Common.ClosedDate']) ?? undefined,\n\t\t\t\t);\n\n\t\t\t\treturn {\n\t\t\t\t\tid: issueResult.id.toString(),\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnodeId: issueResult.id.toString(),\n\t\t\t\t\tprovider: provider,\n\t\t\t\t\tcreatedDate: new Date(issueResult.fields['System.CreatedDate']),\n\t\t\t\t\tupdatedDate: new Date(issueResult.fields['System.ChangedDate']),\n\t\t\t\t\tstate: normalizedState,\n\t\t\t\t\tclosed: normalizedState === 'closed',\n\t\t\t\t\ttitle: issueResult.fields['System.Title'],\n\t\t\t\t\turl: issueResult._links.html.href,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif (options?.type === undefined || options?.type === 'pullrequest') {\n\t\t\ttry {\n\t\t\t\tconst prResult = await this.request<AzurePullRequestWithLinks>(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\toptions?.baseUrl,\n\t\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/pullRequests/${encodeAzurePathSegment(id)}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t},\n\t\t\t\t\tscope,\n\t\t\t\t);\n\n\t\t\t\tif (prResult != null) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: prResult.pullRequestId.toString(),\n\t\t\t\t\t\ttype: 'pullrequest',\n\t\t\t\t\t\tnodeId: prResult.pullRequestId.toString(), // prResult.artifactId maybe?\n\t\t\t\t\t\tprovider: provider,\n\t\t\t\t\t\tcreatedDate: new Date(prResult.creationDate),\n\t\t\t\t\t\tupdatedDate: new Date(prResult.creationDate),\n\t\t\t\t\t\tstate: azurePullRequestStatusToState(prResult.status),\n\t\t\t\t\t\tclosed: isClosedAzurePullRequestStatus(prResult.status),\n\t\t\t\t\t\ttitle: prResult.title,\n\t\t\t\t\t\turl: getAzurePullRequestWebUrl(prResult, options.baseUrl, owner),\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn undefined;\n\t\t\t} catch (ex) {\n\t\t\t\t// A rejected credential is actionable and must not be reported as an absent issue; every other\n\t\t\t\t// non-404 keeps the existing degrade-to-undefined behavior.\n\t\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\t\tif (ex.original?.status !== 404) {\n\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t@trace({\n\t\targs: (provider, token, project, id) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tproject: project,\n\t\t\tid: id,\n\t\t}),\n\t})\n\tpublic async getIssue(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tproject: AzureProjectDescriptor,\n\t\tid: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t): Promise<Issue | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst resource = await this.requireRestResource(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tproject.resourceName,\n\t\t\toptions.baseUrl,\n\t\t\t'workItems',\n\t\t\t'6.0',\n\t\t);\n\n\t\tlet issueResult: WorkItem | undefined;\n\t\ttry {\n\t\t\tissueResult = await this.getWorkItem(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tproject.resourceName,\n\t\t\t\tproject.name,\n\t\t\t\tid,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tresource,\n\t\t\t\tscope,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is actionable and must not be reported as an absent work item; every other\n\t\t\t// non-404 keeps the existing degrade-to-undefined behavior.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tif (ex.original?.status !== 404) {\n\t\t\t\tscope?.error(ex);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\tif (issueResult != null) {\n\t\t\tawait this.requireWorkItemProject(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tproject.resourceName,\n\t\t\t\tproject.name,\n\t\t\t\tissueResult,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tconst issueType = issueResult.fields['System.WorkItemType'];\n\t\t\tconst state = issueResult.fields['System.State'];\n\t\t\tconst [stateCategory, htmlFields] = await Promise.all([\n\t\t\t\tthis.getWorkItemStateCategory(\n\t\t\t\t\tissueType,\n\t\t\t\t\tstate,\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tproject.resourceName,\n\t\t\t\t\tproject.name,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t\tthis.getWorkItemHtmlFields(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tproject.resourceName,\n\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\tissueResult.fields,\n\t\t\t\t\tscope,\n\t\t\t\t),\n\t\t\t]);\n\t\t\treturn fromAzureWorkItem(issueResult, provider, project, stateCategory, 'point', htmlFields);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, rev, baseUrl) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\trev: rev,\n\t\t\tbaseUrl: baseUrl,\n\t\t}),\n\t})\n\tasync getAccountForCommit(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\trev: string,\n\t\tbaseUrl: string,\n\t\t_options?: {\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<UnidentifiedAuthor | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\n\t\ttry {\n\t\t\t// Try to get the Work item (wit) first with specific fields\n\t\t\tconst commit = await this.request<AzureGitCommit>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/commits/${encodeAzurePathSegment(rev)}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tconst author = commit?.author;\n\t\t\tif (!author) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\t// Azure API never gives us an id/username we can use, therefore we always return UnidentifiedAuthor\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tid: undefined,\n\t\t\t\tusername: undefined,\n\t\t\t\tname: author?.name,\n\t\t\t\temail: author?.email,\n\t\t\t\tavatarUrl: undefined,\n\t\t\t} satisfies UnidentifiedAuthor;\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is actionable and must not be reported as an absent work item; every other\n\t\t\t// non-404 keeps the existing degrade-to-undefined behavior.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tif (ex.original?.status !== 404) {\n\t\t\t\tscope?.error(ex);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * The collection an Azure DevOps Server address names, or `undefined` when it names the installation.\n\t *\n\t * Below a collection, `connectionData` describes that collection: its `instanceId` is the collection's id, and its\n\t * `webApplicationRelativeDirectory` ends in the collection's path segment (`DefaultCollection/`); it is empty or\n\t * absent at the server level. As measured against Azure DevOps Server 2020. The directory may also carry the\n\t * installation's own virtual directory before the collection (`tfs/DefaultCollection/`), so only its LAST segment\n\t * names the collection, and only when it is the last segment of the address asked about.\n\t */\n\t@trace({\n\t\targs: (provider, token, baseUrl) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tbaseUrl: baseUrl,\n\t\t}),\n\t})\n\tasync getAddressedCollection(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string,\n\t): Promise<{ id: string; name: string } | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst connectionData = await this.request<{ instanceId?: string; webApplicationRelativeDirectory?: string }>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\t'_apis/connectionData',\n\t\t\t{ method: 'GET' },\n\t\t\tscope,\n\t\t);\n\t\tconst segment = connectionData?.webApplicationRelativeDirectory?.split('/').findLast(Boolean);\n\t\tconst addressed = new URL(baseUrl).pathname.split('/').findLast(Boolean);\n\t\tif (segment == null || addressed == null || connectionData?.instanceId == null) return undefined;\n\n\t\tconst name = decodePathSegment(segment);\n\t\tif (name.toLowerCase() !== decodePathSegment(addressed).toLowerCase()) return undefined;\n\n\t\treturn { id: connectionData.instanceId, name: name };\n\t}\n\n\t@trace({\n\t\targs: (provider, token, baseUrl) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tbaseUrl: baseUrl,\n\t\t}),\n\t})\n\tasync getCurrentUserOnServer(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string,\n\t): Promise<{ id: string; name?: string; email?: string; username?: string; avatarUrl?: string } | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst connectionData = await this.request<{\n\t\t\t\tauthenticatedUser?: {\n\t\t\t\t\tid: string;\n\t\t\t\t\tdescriptor: string;\n\t\t\t\t\tisActive: boolean;\n\t\t\t\t\tmetTypeId: number;\n\t\t\t\t\tproviderDisplayName?: string;\n\t\t\t\t\temailAddress?: string;\n\t\t\t\t\tresourceVersion: 2;\n\t\t\t\t\tsubjectDescriptor: string;\n\t\t\t\t\tproperties?: {\n\t\t\t\t\t\tAccount?: {\n\t\t\t\t\t\t\t$type: string;\n\t\t\t\t\t\t\t$value: string;\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\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t'_apis/connectionData',\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t},\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tconst user = connectionData?.authenticatedUser;\n\t\t\tconst username = user?.properties?.Account?.$value;\n\t\t\tif (!username) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tid: user.id,\n\t\t\t\tname: user.providerDisplayName,\n\t\t\t\temail: user.emailAddress,\n\t\t\t\tusername: username,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to get current user from ${baseUrl}`);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tasync getWorkItemStateCategory(\n\t\tissueType: string,\n\t\tstate: string,\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tprojectName: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureWorkItemStateCategory | undefined> {\n\t\tconst states = await this.getWorkItemTypeStates(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\towner,\n\t\t\tprojectName,\n\t\t\tissueType,\n\t\t\toptions.baseUrl,\n\t\t\tcancellation,\n\t\t);\n\t\treturn resolveAzureWorkItemStateCategory(states, state);\n\t}\n\n\tprivate getWorkItemTypeStates(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tproject: string,\n\t\ttype: string,\n\t\tbaseUrl: string,\n\t\tcancellation?: AbortSignal,\n\t) {\n\t\treturn this._workItemStates.getStates(\n\t\t\t{ baseUrl: baseUrl, namespace: owner, project: project, token: token.accessToken, isPAT: true },\n\t\t\ttype,\n\t\t\tasync signal => {\n\t\t\t\tconst resource = await this.requireRestResource(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\towner,\n\t\t\t\t\tbaseUrl,\n\t\t\t\t\t'workItemTypeStates',\n\t\t\t\t\t'6.0-preview.1',\n\t\t\t\t\tsignal,\n\t\t\t\t);\n\t\t\t\treturn this.request(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tbaseUrl,\n\t\t\t\t\tgetAzureWorkItemTypeStatesPath(owner, project, type, resource.apiVersion),\n\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\tundefined,\n\t\t\t\t\tsignal,\n\t\t\t\t);\n\t\t\t},\n\t\t\tcancellation,\n\t\t\tasync signal => {\n\t\t\t\tconst resource = await this.requireRestResource(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\towner,\n\t\t\t\t\tbaseUrl,\n\t\t\t\t\t'backlogConfiguration',\n\t\t\t\t\t'6.0-preview.1',\n\t\t\t\t\tsignal,\n\t\t\t\t);\n\t\t\t\tconst path = `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(project)}/_apis/work/backlogconfiguration?api-version=${resource.apiVersion}`;\n\t\t\t\treturn this.request(provider, token, baseUrl, path, { method: 'GET' }, undefined, signal);\n\t\t\t},\n\t\t);\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t}),\n\t})\n\tasync getRepositoryMetadata(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst resource = await this.requireRestResource(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\towner,\n\t\t\toptions.baseUrl,\n\t\t\t'repositories',\n\t\t\t'7.1',\n\t\t\tcancellation,\n\t\t);\n\n\t\ttry {\n\t\t\tconst response = await this.getRepository(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\towner,\n\t\t\t\trepo,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tresource.apiVersion,\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tif (response == null) return undefined;\n\n\t\t\t// Azure's `parentRepository` only reliably carries the repo name and its project; a fork's parent\n\t\t\t// lives in the same organization, so the org (`owner`) is the parent owner. Only report `parent`\n\t\t\t// when the parent's name is actually present — never fall back to the fork's own name.\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\towner: owner,\n\t\t\t\t// Prefer the API's canonical name over parsing the composite `repo` descriptor string.\n\t\t\t\tname: response.name,\n\t\t\t\tisFork: response.isFork ?? false,\n\t\t\t\tparent:\n\t\t\t\t\tresponse.isFork && response.parentRepository?.name != null\n\t\t\t\t\t\t? { owner: owner, name: response.parentRepository.name }\n\t\t\t\t\t\t: undefined,\n\t\t\t} satisfies RepositoryMetadata;\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is not a probe outcome: it is actionable, and the caller routes it into the\n\t\t\t// session recovery. A cancellation or a 404 still degrades quietly.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\t// Cancellations and 404s are expected outcomes for a probe; don't log them as errors.\n\t\t\tif (!isCancellationError(ex) && !(ex instanceof RequestNotFoundError)) {\n\t\t\t\tscope?.error(ex);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t}),\n\t})\n\tasync getDefaultBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\toptions: {\n\t\t\tbaseUrl: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<DefaultBranch | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst resource = await this.requireRestResource(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\towner,\n\t\t\toptions.baseUrl,\n\t\t\t'repositories',\n\t\t\t'7.1',\n\t\t\tcancellation,\n\t\t);\n\n\t\ttry {\n\t\t\tconst response = await this.getRepository(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\towner,\n\t\t\t\trepo,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tresource.apiVersion,\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tif (response?.defaultBranch == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tname: normalizeAzureBranchName(response.defaultBranch),\n\t\t\t} satisfies DefaultBranch;\n\t\t} catch (ex) {\n\t\t\t// A rejected credential is not a probe outcome: it is actionable, and the caller routes it into the\n\t\t\t// session recovery. A cancellation or a 404 still degrades quietly.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\t// Cancellations and 404s are expected outcomes for a probe; don't log them as errors.\n\t\t\tif (!isCancellationError(ex) && !(ex instanceof RequestNotFoundError)) {\n\t\t\t\tscope?.error(ex);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * One page of a filtered work-item search over a collection: bounded WIQL queries for the ordered ids, then one\n\t * detail read for the page's slice of them.\n\t *\n\t * The ids a first page queried are kept as the snapshot its continuations page through, for as long as the\n\t * pagination keeps reading it, so metadata changes cannot retarget the continuation. Each first page queries its\n\t * own snapshot, so paginations never share one. A continuation whose snapshot is gone is refused (see\n\t * {@link AzureWorkItemSearchCursor}).\n\t *\n\t * The query asks for one id more than {@link azureWorkItemSearchResultLimit}, bounded by `$top`: the extra id is\n\t * what tells \"exactly the limit\" from \"past it\". Past it, the page is `truncated` with `limitReached` and no total,\n\t * since the true count is unknowable here. A server that refuses the match set even so fails the page with a\n\t * \"narrow the search\" error: no bounded query could serve that order's first window.\n\t *\n\t * `resolveProject` maps each result's project name to its descriptor. A work item in a project it doesn't know\n\t * fails the page rather than being dropped or mis-attributed, since either would leave the page disagreeing with\n\t * the count of the same query; the caller refreshes its project discovery before a search so that means a\n\t * project created during the pagination.\n\t */\n\t@trace({\n\t\targs: (provider, token, collection) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tcollection: collection,\n\t\t}),\n\t})\n\tasync searchWorkItemsPage(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\toptions: WorkItemSearchOptions & {\n\t\t\tresolveProject: (name: string) => AzureProjectDescriptor | undefined;\n\t\t\tcursor?: string;\n\t\t\tpageSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<ProviderIssueSearchPage> {\n\t\tconst scope = getScopedLogger();\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst identity = JSON.stringify([\n\t\t\toptions.criteria?.state ?? 'open',\n\t\t\ttoAzureWorkItemSearchWiql(\n\t\t\t\t{ ...options.criteria, state: 'all' },\n\t\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\t\toptions.projectNames,\n\t\t\t),\n\t\t]);\n\t\tconst key = toAzureWorkItemSearchCursorKey(collection, identity);\n\t\tconst pageSize = toAzureSearchPageSize(options.pageSize, 100, azureWorkItemBatchLimit);\n\t\tconst cursor = parseAzureWorkItemSearchCursor(options.cursor, key, pageSize);\n\t\tconst page = cursor?.page ?? 1;\n\n\t\t// Keep the full request identity: metadata changes cannot retarget a live snapshot, and a hash collision\n\t\t// cannot let a different query or credential read it.\n\t\tconst snapshot = cursor?.snapshot ?? uuid();\n\t\tconst snapshotKey = JSON.stringify([\n\t\t\tawait sha256(token.accessToken),\n\t\t\toptions.baseUrl,\n\t\t\tcollection,\n\t\t\tidentity,\n\t\t\tsnapshot,\n\t\t]);\n\t\t// A continuation only ever READS its snapshot: resuming against a fresh query could skip a work item silently.\n\t\tconst snapshotFound = cursor?.snapshot != null ? this._workItemSearches.get(snapshotKey) : undefined;\n\t\tif (cursor?.snapshot != null && snapshotFound == null) {\n\t\t\tthrow new Error('Work item search results expired; restart the read without a cursor');\n\t\t}\n\n\t\tconst result = await (snapshotFound ??\n\t\t\tthis._workItemSearches.getOrCreate(\n\t\t\t\tsnapshotKey,\n\t\t\t\t(_cacheable, signal) =>\n\t\t\t\t\tthis.readWorkItemSearch(provider, token, collection, options, true, scope, signal).catch(\n\t\t\t\t\t\t(ex: unknown) => {\n\t\t\t\t\t\t\tif (isWorkItemLimitRefusal(ex)) {\n\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t`${provider.name} refused a work item search matching more than ${azureWorkItemSearchResultLimit} results; narrow the search`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t{ cancellation: cancellation },\n\t\t\t));\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst limitReached = result.length > azureWorkItemSearchResultLimit;\n\t\tconst ids = limitReached ? result.slice(0, azureWorkItemSearchResultLimit) : result;\n\n\t\tconst offset = cursor?.offset ?? 0;\n\n\t\tconst slice = ids.slice(offset, offset + pageSize);\n\t\tconst values: Issue[] = [];\n\t\tif (slice.length > 0) {\n\t\t\tconst batch = await this.getRestResource(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tcollection,\n\t\t\t\toptions.baseUrl,\n\t\t\t\t'workItemsBatch',\n\t\t\t\t'5.0',\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tconst resource =\n\t\t\t\tbatch ??\n\t\t\t\t(await this.requireRestResource(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tcollection,\n\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\t'workItems',\n\t\t\t\t\t'5.0',\n\t\t\t\t\tcancellation,\n\t\t\t\t));\n\t\t\tconst route =\n\t\t\t\tbatch != null\n\t\t\t\t\t? `workitemsbatch?api-version=${resource.apiVersion}`\n\t\t\t\t\t: `workitems?ids=${slice.join(',')}&$expand=Links&errorPolicy=Omit&api-version=${resource.apiVersion}`;\n\t\t\tconst workItems = await this.request<{ value: (WorkItem | null)[] }>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\t`${encodeAzurePathSegment(collection)}/_apis/wit/${route}`,\n\t\t\t\tbatch != null\n\t\t\t\t\t? { method: 'POST', body: JSON.stringify({ ids: slice, $expand: 'Links', errorPolicy: 'Omit' }) }\n\t\t\t\t\t: { method: 'GET' },\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tconst byId = new Map((workItems?.value ?? []).filter(w => w != null).map(w => [w.id, w]));\n\t\t\tfor (const id of slice) {\n\t\t\t\t// A work item deleted since the ids were queried is gone, not a failure: skipping it keeps the page\n\t\t\t\t// honest, since it no longer matches anything.\n\t\t\t\tconst workItem = byId.get(id);\n\t\t\t\tif (workItem == null) continue;\n\n\t\t\t\tconst projectName = workItem.fields['System.TeamProject'];\n\t\t\t\tif (\n\t\t\t\t\toptions.projectNames?.length &&\n\t\t\t\t\t!options.projectNames.some(name => name.toLowerCase() === projectName?.toLowerCase())\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(`Azure DevOps work item ${id} is outside the requested projects`);\n\t\t\t\t}\n\n\t\t\t\tconst project = options.resolveProject(projectName);\n\t\t\t\tif (project == null) {\n\t\t\t\t\tthrow new Error(`Azure DevOps work item ${id} is in project '${projectName}', which isn't visible`);\n\t\t\t\t}\n\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\tconst stateCategory = await this.getWorkItemStateCategory(\n\t\t\t\t\tworkItem.fields['System.WorkItemType'],\n\t\t\t\t\tworkItem.fields['System.State'],\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tcollection,\n\t\t\t\t\tproject.name,\n\t\t\t\t\toptions,\n\t\t\t\t\tcancellation,\n\t\t\t\t);\n\t\t\t\tconst htmlFields = await this.getWorkItemHtmlFields(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tcollection,\n\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\tworkItem.fields,\n\t\t\t\t\tscope,\n\t\t\t\t\tcancellation,\n\t\t\t\t);\n\t\t\t\tvalues.push(fromAzureWorkItem(workItem, provider, project, stateCategory, 'search', htmlFields));\n\t\t\t}\n\t\t}\n\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst nextOffset = offset + slice.length;\n\t\tconst hasMore = nextOffset < ids.length;\n\t\tconst next: AzureWorkItemSearchCursor = { key: key, snapshot: snapshot, offset: nextOffset, page: page + 1 };\n\t\treturn {\n\t\t\tvalues: values,\n\t\t\tcursor: hasMore ? JSON.stringify(next) : undefined,\n\t\t\thasMore: hasMore,\n\t\t\tpage: page,\n\t\t\ttruncated: limitReached,\n\t\t\ttotalCount: limitReached ? undefined : ids.length,\n\t\t\tlimitReached: limitReached || undefined,\n\t\t};\n\t}\n\n\t/**\n\t * How many work items the same WIQL {@link searchWorkItemsPage} would page through match, reading no details.\n\t *\n\t * Asks for one id more than the result ceiling, so the count is exact up to it and `'exceeds-limit'` past it —\n\t * never the ceiling itself, which would understate the match set.\n\t */\n\t@trace({\n\t\targs: (provider, token, collection) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tcollection: collection,\n\t\t}),\n\t})\n\tasync countWorkItems(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\toptions: WorkItemSearchOptions,\n\t\tcancellation?: AbortSignal,\n\t): Promise<number | 'exceeds-limit'> {\n\t\tconst scope = getScopedLogger();\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\ttry {\n\t\t\tconst result = await this.readWorkItemSearch(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tcollection,\n\t\t\t\toptions,\n\t\t\t\tfalse,\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\treturn result.length > azureWorkItemSearchResultLimit ? 'exceeds-limit' : result.length;\n\t\t} catch (ex) {\n\t\t\tif (isWorkItemLimitRefusal(ex)) return 'exceeds-limit';\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate async readWorkItemSearch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\toptions: WorkItemSearchOptions,\n\t\tordered: boolean,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<number[]> {\n\t\tconst queries = await this.getWorkItemSearchQueries(provider, token, collection, options, cancellation);\n\t\treturn queryAzureWorkItemSearch(\n\t\t\tqueries,\n\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\tordered,\n\t\t\t(wiql, top, asOf) =>\n\t\t\t\tthis.queryWorkItemIds(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\tcollection,\n\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\twiql,\n\t\t\t\t\ttop,\n\t\t\t\t\tscope,\n\t\t\t\t\tcancellation,\n\t\t\t\t\tasOf,\n\t\t\t\t),\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async getWorkItemSearchQueries(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\toptions: WorkItemSearchOptions,\n\t\tcancellation?: AbortSignal,\n\t): Promise<string[]> {\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tconst projectNames = options.projectNames ?? options.workflowProjectNames ?? [];\n\t\tlet workflows: Map<string, AzureWorkItemTypeStates> | undefined;\n\t\tif (options.criteria?.state !== 'all') {\n\t\t\tconst types = (\n\t\t\t\tawait mapBounded(projectNames, workItemMetadataConcurrency, async project => {\n\t\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\t\tconst scope = {\n\t\t\t\t\t\tbaseUrl: options.baseUrl,\n\t\t\t\t\t\tnamespace: collection,\n\t\t\t\t\t\tproject: project,\n\t\t\t\t\t\ttoken: token.accessToken,\n\t\t\t\t\t\tisPAT: true,\n\t\t\t\t\t};\n\t\t\t\t\tconst names = await this._workItemStates.getTypes(\n\t\t\t\t\t\tscope,\n\t\t\t\t\t\tasync signal => {\n\t\t\t\t\t\t\tconst resource = await this.requireRestResource(\n\t\t\t\t\t\t\t\tprovider,\n\t\t\t\t\t\t\t\ttoken,\n\t\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\t\t\t\t'workItemTypes',\n\t\t\t\t\t\t\t\t'6.0',\n\t\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn this.request(\n\t\t\t\t\t\t\t\tprovider,\n\t\t\t\t\t\t\t\ttoken,\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tgetAzureWorkItemTypesUrl(options.baseUrl, collection, project, resource.apiVersion),\n\t\t\t\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcancellation,\n\t\t\t\t\t);\n\t\t\t\t\treturn names.map(name => ({ project: project, name: name, scope: scope }));\n\t\t\t\t})\n\t\t\t).flat();\n\t\t\tconst states = await mapBounded(types, workItemMetadataConcurrency, async type => ({\n\t\t\t\tname: type.name,\n\t\t\t\tproject: type.project,\n\t\t\t\tstates: requireAzureWorkItemStateCategories(\n\t\t\t\t\tawait this.getWorkItemTypeStates(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\ttoken,\n\t\t\t\t\t\tcollection,\n\t\t\t\t\t\ttype.project,\n\t\t\t\t\t\ttype.name,\n\t\t\t\t\t\toptions.baseUrl,\n\t\t\t\t\t\tcancellation,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t}));\n\t\t\tworkflows = new Map(\n\t\t\t\tprojectNames.map(project => [project, { types: states.filter(type => type.project === project) }]),\n\t\t\t);\n\t\t}\n\n\t\treturn toAzureWorkItemSearchQueries(\n\t\t\toptions.criteria,\n\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\tprojectNames,\n\t\t\tworkflows,\n\t\t);\n\t}\n\n\t/**\n\t * The ids a collection-level WIQL query matches, in its order.\n\t *\n\t * `timePrecision=true` lets the criteria's dates compare as instants, including on legacy servers.\n\t */\n\tprivate async queryWorkItemIds(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tcollection: string,\n\t\tbaseUrl: string,\n\t\twiql: string,\n\t\ttop: number,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t\tasOf?: string,\n\t): Promise<{ ids: number[]; asOf?: string }> {\n\t\tconst query = asOf == null ? wiql : `${wiql} ASOF '${asOf}'`;\n\t\tif (query.length > azureWorkItemWiqlLengthLimit) {\n\t\t\tthrow new Error('Azure DevOps work item search query is too long; narrow the search');\n\t\t}\n\n\t\tconst resource = await this.requireRestResource(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tcollection,\n\t\t\tbaseUrl,\n\t\t\t'wiql',\n\t\t\t'5.0',\n\t\t\tcancellation,\n\t\t);\n\t\tconst result = await this.request<{ workItems?: { id: number }[]; asOf?: string }>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\t`${encodeAzurePathSegment(collection)}/_apis/wit/wiql?$top=${top}&timePrecision=true&api-version=${resource.apiVersion}`,\n\t\t\t{ method: 'POST', body: JSON.stringify({ query: query }) },\n\t\t\tscope,\n\t\t\tcancellation,\n\t\t);\n\t\tif (result?.workItems == null) throw new Error('Azure DevOps returned no work item query result');\n\n\t\treturn { ids: result.workItems.map(w => w.id), asOf: result.asOf };\n\t}\n\n\tprivate async getRepository(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tbaseUrl: string,\n\t\tapiVersion: string,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureRepositoryWithMetadata | undefined> {\n\t\tconst { projectName, repoName } = parseAzureRepositoryDescriptor(repo);\n\t\tconst encodedProject = encodeAzurePathSegment(projectName);\n\t\tconst encodedRepo = encodeAzurePathSegment(repoName);\n\t\treturn this.request<AzureRepositoryWithMetadata>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\t`${encodeAzurePathSegment(owner)}/${encodedProject}/_apis/git/repositories/${encodedRepo}?api-version=${apiVersion}`,\n\t\t\t{ method: 'GET' },\n\t\t\tscope,\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async toPullRequest(\n\t\tpr: AzurePullRequest,\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tbaseUrl: string,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest> {\n\t\tconst forkRepositoryUrls = await this.getForkRepositoryUrls(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\towner,\n\t\t\tbaseUrl,\n\t\t\tpr.forkSource?.repository,\n\t\t\tscope,\n\t\t\tcancellation,\n\t\t);\n\t\t// Only the point reads convert through here.\n\t\treturn fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls, 'point');\n\t}\n\n\t/**\n\t * Resolves the web and HTTPS clone URLs of the fork a cross-repository pull request comes from. Every other\n\t * repository URL the model reports is rebuilt from the pull request payload; only a fork reference lacks the\n\t * project to do that, so only a fork costs a request.\n\t *\n\t * Best-effort by contract: a fork in a project the token cannot read, a deleted fork, or a throttled request\n\t * leaves the head ref without a URL. It must never cost the pull request itself, which is what makes swallowing\n\t * the failure here the right call rather than a shortcut.\n\t */\n\tprivate async getForkRepositoryUrls(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tbaseUrl: string,\n\t\trepository: AzureRepositoryReference | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureForkRepositoryUrls | undefined> {\n\t\tif (repository == null) return undefined;\n\n\t\tlet tokenHash: string;\n\t\ttry {\n\t\t\ttokenHash = await sha256(token.accessToken);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst cacheKey = JSON.stringify([tokenHash, baseUrl, owner, repository.id]);\n\t\t// The request is shared, so the factory is handed the cache's aggregate signal rather than this caller's: it\n\t\t// aborts only once every caller waiting on the entry has cancelled.\n\t\treturn this._forkRepositoryUrls.getOrCreate(\n\t\t\tcacheKey,\n\t\t\t(cacheable, aggregate) =>\n\t\t\t\tthis.fetchForkRepositoryUrls(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\towner,\n\t\t\t\t\tbaseUrl,\n\t\t\t\t\trepository.id,\n\t\t\t\t\tcacheable,\n\t\t\t\t\tscope,\n\t\t\t\t\taggregate,\n\t\t\t\t),\n\t\t\t{ cancellation: cancellation },\n\t\t);\n\t}\n\n\tprivate async fetchForkRepositoryUrls(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tbaseUrl: string,\n\t\trepositoryId: string,\n\t\tcacheable: CacheController,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureForkRepositoryUrls | undefined> {\n\t\ttry {\n\t\t\tencodeAzurePathSegment(repositoryId);\n\t\t\tconst resource = await this.requireRestResource(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\towner,\n\t\t\t\tbaseUrl,\n\t\t\t\t'repositories',\n\t\t\t\t'4.1',\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tconst response = await this.request<AzureRepositoryUrls>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\t`${encodeAzurePathSegment(owner)}/_apis/git/repositories/${encodeAzurePathSegment(repositoryId)}?api-version=${resource.apiVersion}`,\n\t\t\t\t{ method: 'GET' },\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tconst webUrl = sanitizeAzureRepositoryUrl(response?.webUrl, baseUrl, owner);\n\t\t\tconst cloneHttps = sanitizeAzureRepositoryUrl(response?.remoteUrl, baseUrl, owner);\n\n\t\t\t// The clone URL stands in for the web URL when the response omits it — an older `api-version` promises\n\t\t\t// neither field, and naming the fork by the URL git would use beats naming it not at all.\n\t\t\tconst url = webUrl ?? cloneHttps;\n\t\t\tif (url == null) {\n\t\t\t\tcacheable.invalidate();\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\treturn { url: url, cloneHttps: cloneHttps };\n\t\t} catch (ex) {\n\t\t\t// Missing or inaccessible forks are cached briefly; transient failures retry on the next read.\n\t\t\tif (!(ex instanceof RequestNotFoundError) && !(ex instanceof AuthenticationError)) {\n\t\t\t\tcacheable.invalidate();\n\t\t\t}\n\n\t\t\tconst status = ex instanceof ProviderFetchError ? ` (${ex.status})` : '';\n\t\t\tscope?.warn(`Unable to resolve the fork repository URLs${status}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async getWorkItem(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tproject: string,\n\t\tid: string,\n\t\tbaseUrl: string,\n\t\tresource: AzureDevOpsRestResourceInfo,\n\t\tscope: ScopedLogger | undefined,\n\t): Promise<WorkItem | undefined> {\n\t\tconst encodedProject = encodeAzurePathSegment(project);\n\t\tconst encodedId = encodeAzurePathSegment(id);\n\t\tconst projectPath = resource.projectScoped ? `/${encodedProject}` : '';\n\t\treturn this.request<WorkItem>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\t`${encodeAzurePathSegment(owner)}${projectPath}/_apis/wit/workItems/${encodedId}?api-version=${resource.apiVersion}`,\n\t\t\t{ method: 'GET' },\n\t\t\tscope,\n\t\t);\n\t}\n\n\tprivate async requireWorkItemProject(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\tproject: string,\n\t\tworkItem: WorkItem,\n\t\tbaseUrl: string,\n\t\tscope: ScopedLogger | undefined,\n\t): Promise<void> {\n\t\tconst actualProject = workItem.fields?.['System.TeamProject'];\n\t\tif (typeof actualProject !== 'string' || !actualProject.trim()) {\n\t\t\tthrow new Error(`Azure DevOps work item ${workItem.id} has no project`);\n\t\t}\n\n\t\tif (actualProject.toLowerCase() === project.toLowerCase()) return;\n\n\t\tconst canonical = await this.request<{ name: string }>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\t`${encodeAzurePathSegment(owner)}/_apis/projects/${encodeAzurePathSegment(project)}`,\n\t\t\t{ method: 'GET' },\n\t\t\tscope,\n\t\t);\n\t\tif (canonical?.name?.toLowerCase() !== actualProject.toLowerCase()) {\n\t\t\tthrow new Error(`Azure DevOps work item ${workItem.id} is outside the requested project`);\n\t\t}\n\t}\n\n\tprivate async getWorkItemHtmlFields(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tnamespace: string,\n\t\tbaseUrl: string,\n\t\tfields: WorkItem['fields'],\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal,\n\t): Promise<readonly AzureWorkItemHtmlField[]> {\n\t\ttry {\n\t\t\tconst requestToken = { ...token };\n\t\t\treturn await this._workItemDescriptions.getFields(\n\t\t\t\tfields,\n\t\t\t\t{ baseUrl: baseUrl, namespace: namespace, token: requestToken.accessToken, isPAT: true },\n\t\t\t\tasync signal => {\n\t\t\t\t\tconst resource = await this.requireRestResource(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\trequestToken,\n\t\t\t\t\t\tnamespace,\n\t\t\t\t\t\tbaseUrl,\n\t\t\t\t\t\t'fields',\n\t\t\t\t\t\t'6.0',\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t);\n\t\t\t\t\treturn this.request(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\trequestToken,\n\t\t\t\t\t\tbaseUrl,\n\t\t\t\t\t\t`${encodeAzurePathSegment(namespace)}/_apis/wit/fields?api-version=${resource.apiVersion}`,\n\t\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tif (isCancellationError(ex) && cancellation?.aborted) throw ex;\n\n\t\t\tscope?.warn(`Unable to read the Azure DevOps field catalog of '${namespace}': ${ex}`);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tprivate async getRestResource(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tnamespace: string,\n\t\tbaseUrl: string,\n\t\tresource: AzureDevOpsRestResource,\n\t\tversion: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureDevOpsRestResourceInfo | undefined> {\n\t\ttry {\n\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\tencodeAzurePathSegment(namespace);\n\t\t\tconst discovery = this.config.wrapForForcedInsecureSSL(provider.getIgnoreSSLErrors(), () =>\n\t\t\t\tthis.restApi.getRestApiResource(\n\t\t\t\t\t{ namespace: namespace, resource: resource, version: version },\n\t\t\t\t\t{ token: token.accessToken, isPAT: true, baseUrl: baseUrl },\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (cancellation?.aborted) {\n\t\t\t\t// The race skips observing discovery when setup aborts its caller synchronously.\n\t\t\t\tvoid discovery.catch(() => {});\n\t\t\t}\n\n\t\t\treturn await (cancellation != null ? raceWithSignal(discovery, cancellation) : discovery);\n\t\t} catch (ex) {\n\t\t\treturn throwProviderError(token, ex);\n\t\t}\n\t}\n\n\tprivate async requireRestResource(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tnamespace: string,\n\t\tbaseUrl: string,\n\t\tresource: AzureDevOpsRestResource,\n\t\tversion: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureDevOpsRestResourceInfo> {\n\t\tlet result: AzureDevOpsRestResourceInfo | undefined;\n\t\ttry {\n\t\t\tresult = await this.getRestResource(provider, token, namespace, baseUrl, resource, version, cancellation);\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) {\n\t\t\t\tthrow new Error('Azure DevOps REST resource discovery failed', { cause: ex });\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t\tif (result == null) throw new Error(`Azure DevOps REST resource ${resource} is unavailable`);\n\n\t\treturn result;\n\t}\n\n\tprivate async request<T>(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string | undefined,\n\t\troute: string,\n\t\toptions: { method: RequestInit['method'] } & Record<string, unknown>,\n\t\tscope: ScopedLogger | undefined,\n\t\tcancellation?: AbortSignal | undefined,\n\t): Promise<T | undefined> {\n\t\tconst { accessToken } = token;\n\t\tconst url = baseUrl ? `${baseUrl}/${route}` : route;\n\n\t\tlet rsp: Response;\n\t\ttry {\n\t\t\tconst sw = maybeStopWatch(`[AZURE] ${options?.method ?? 'GET'} request`, { log: { onlyExit: true } });\n\n\t\t\ttry {\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\trsp = await this.config.wrapForForcedInsecureSSL(provider.getIgnoreSSLErrors(), () =>\n\t\t\t\t\tthis.config.fetch(url, {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAuthorization: `Basic ${base64(`PAT:${accessToken}`)}`,\n\t\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsignal: cancellation,\n\t\t\t\t\t\t...options,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\tif (rsp.ok) {\n\t\t\t\t\t// Azure answers a rejected credential by redirecting to its sign-in page, which returns `203\n\t\t\t\t\t// text/html` rather than `401`. `rsp.ok` spans 200-299, so that page used to reach `json()` and\n\t\t\t\t\t// die as a bare `SyntaxError` — which `getIssue` and friends catch and report as \"not found\",\n\t\t\t\t\t// making an invalid credential indistinguishable from a missing work item (GKDEV-3617).\n\t\t\t\t\tconst contentType = rsp.headers.get('content-type')?.toLowerCase() ?? '';\n\t\t\t\t\tif (contentType.startsWith('text/html')) {\n\t\t\t\t\t\tconst { accessToken: _accessToken, ...tokenInfo } = token;\n\t\t\t\t\t\tthrow new AuthenticationError(\n\t\t\t\t\t\t\ttokenInfo,\n\t\t\t\t\t\t\tAuthenticationErrorReason.Unauthorized,\n\t\t\t\t\t\t\tnew Error(`(${rsp.status}) Azure DevOps returned a sign-in page instead of data`),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn (await rsp.json()) as T;\n\t\t\t\t}\n\n\t\t\t\t// Reads the body so the 403 branch below can tell a throttled request from a permission failure.\n\t\t\t\tthrow await ProviderFetchError.fromResponse('AzureDevOps', rsp);\n\t\t\t} finally {\n\t\t\t\tsw?.stop();\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof ProviderFetchError || ex.name === 'AbortError') {\n\t\t\t\tthis.handleRequestError(provider, token, ex, scope);\n\t\t\t} else if (Logger.isDebugging) {\n\t\t\t\tthis.config.onError?.(`AzureDevOps request failed: ${ex.message}`);\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate handleRequestError(\n\t\tprovider: Provider | undefined,\n\t\ttoken: TokenWithInfo,\n\t\tex: ProviderFetchError | (Error & { name: 'AbortError' }),\n\t\tscope: ScopedLogger | undefined,\n\t): void {\n\t\tif (ex.name === 'AbortError' || !(ex instanceof ProviderFetchError)) throw new CancellationError(ex);\n\n\t\tconst { accessToken, ...tokenInfo } = token;\n\t\tswitch (ex.status) {\n\t\t\tcase 404: // Not found\n\t\t\tcase 410: // Gone\n\t\t\tcase 422: // Unprocessable Entity\n\t\t\t\tthrow new RequestNotFoundError(ex);\n\t\t\tcase 401: // Unauthorized\n\t\t\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Unauthorized, ex);\n\t\t\tcase 429: // Too Many Requests\n\t\t\t\tthrow toRateLimitError(ex, accessToken);\n\t\t\tcase 403: // Forbidden\n\t\t\t\t// Azure returns 403 for both a permission failure and a throttled request (\"Request was blocked\n\t\t\t\t// due to exceeding usage of resource 'RateLimit'\"), so the message is the discriminant. Reporting\n\t\t\t\t// a throttle as `auth` would prompt the user to re-authenticate a healthy connection instead of\n\t\t\t\t// retrying (see `isRateLimitResponse`).\n\t\t\t\tif (isRateLimitResponse(ex)) throw toRateLimitError(ex, accessToken);\n\n\t\t\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Forbidden, ex);\n\t\t\tcase 500: // Internal Server Error\n\t\t\t\tscope?.error(ex);\n\t\t\t\tif (ex.response != null) {\n\t\t\t\t\tthis.config.onRequestFailed?.(\n\t\t\t\t\t\tprovider == null || provider.id === 'azure'\n\t\t\t\t\t\t\t? l10n.t(\n\t\t\t\t\t\t\t\t\t'{0} failed to respond and might be experiencing issues. Please visit the [AzureDevOps status page](https://status.dev.azure.com) for more information.',\n\t\t\t\t\t\t\t\t\tprovider?.name ?? 'AzureDevOps',\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: l10n.t('{0} failed to respond and might be experiencing issues.', provider.name),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 502: // Bad Gateway\n\t\t\t\tscope?.error(ex);\n\t\t\t\t// TODO: Learn the Azure API docs and put it in order:\n\t\t\t\t// if (ex.message.includes('timeout')) {\n\t\t\t\t// \tprovider?.trackRequestException();\n\t\t\t\t// \tvoid showIntegrationRequestTimedOutWarningMessage(provider?.name ?? 'Azure');\n\t\t\t\t// \treturn;\n\t\t\t\t// }\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (ex.status >= 400 && ex.status < 500) throw new RequestClientError(ex);\n\t\t\t\tbreak;\n\t\t}\n\n\t\tscope?.error(ex);\n\t\tif (Logger.isDebugging) {\n\t\t\tthis.config.onError?.(\n\t\t\t\t`AzureDevOps request failed: ${(ex.response as any)?.errors?.[0]?.message ?? ex.message}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** Wires an {@link AzureDevOpsApi} from the full runtime context, mapping `ctx` down to the narrow config. */\nexport function createAzureDevOpsApi(\n\tctx: IntegrationServiceContext,\n\tworkItemStates?: AzureWorkItemStatesCache,\n\tworkItemDescriptions?: AzureWorkItemDescriptionsCache,\n): AzureDevOpsApi {\n\treturn new AzureDevOpsApi(baseProviderApiConfig(ctx), workItemStates, workItemDescriptions);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAyFA,MAAM,4BAA4B;AAClC,MAAM,8BAA8B;;AAEpC,MAAM,4BAA4B;AAClC,MAAM,+BAA+B;;AAUrC,SAAS,uBAAuB,IAAsB;CACrD,OAAO,cAAc,sBAAsB,GAAG,QAAQ,SAAS,UAAU;AAC1E;AAEA,SAAS,+BAA+B,MAAyD;CAChG,MAAM,QAAQ,KAAK,MAAM,GAAG;CAC5B,MAAM,CAAC,aAAa,SAAS,YAAY;CACzC,IAAI,MAAM,WAAW,KAAK,YAAY,UAAU,CAAC,eAAe,CAAC,UAChE,MAAM,IAAI,MAAM,wCAAwC,KAAK,yCAAyC;CAGvG,OAAO;EAAe;EAAuB;CAAS;AACvD;AAEA,IAAa,iBAAb,MAAkD;CAuB/B;CAtBlB;CACA;CACA;CACA;CACA;CACA;CACA,sBAAuC,IAAI,aAA0D;EACpG,UAAU;EACV,WAAW;CACZ,CAAC;;;;;;CAMD,oBAAqC,IAAI,aAA+B;EACvE,UAAU;EACV,WAAW;EACX,WAAW;CACZ,CAAC;CAED,YACC,QACA,gBACA,sBACC;EAHgB,KAAA,SAAA;EAIjB,KAAK,UAAU,mBAAmB,EACjC,SAAS,0BAA0B,OAAO,SAAS,OAAO,MAAM,OAAO,IAAI,CAAC,EAC7E,CAAC,CAAC,CAAC;EACH,KAAK,kBAAkB,kBAAkB,IAAI,yBAAyB;EACtE,KAAK,qBAAqB,kBAAkB;EAC5C,KAAK,wBAAwB,wBAAwB,IAAI,+BAA+B;EACxF,KAAK,2BAA2B,wBAAwB;EACxD,KAAK,cAAc,OAAO,wBAAwB,KAAK,YAAY,CAAC;CACrE;CAEA,UAAgB;EACf,KAAK,aAAa,QAAQ;CAC3B;CAEA,cAA4B;EAC3B,KAAK,QAAQ,aAAa,CAAC,CAAC;EAC5B,IAAI,KAAK,oBACR,KAAK,gBAAgB,MAAM;EAE5B,IAAI,KAAK,0BACR,KAAK,sBAAsB,MAAM;EAElC,KAAK,oBAAoB,MAAM;EAC/B,KAAK,kBAAkB,MAAM;CAC9B;CAEA,MASa,wBACZ,UACA,OACA,OACA,MACA,QACA,SAGmC;EACnC,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,EAAE,aAAa,aAAa,+BAA+B,IAAI;EAErE,IAAI;GA8BH,MAAM,OAjBY,MAZK,KAAK,QAC3B,UACA,OACA,SAAS,SACT,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,WAAW,EAAE,0BAA0B,uBAAuB,QAAQ,EAAE,uEAAuE,mBAAmB,cAAc,QAAQ,KACnP,EACC,QAAQ,MACT,GACA,KACD,EAAA,EAG4B,MAAM,MAAM,GAAG,MAAM;IAEhD,MAAM,UAAU,EAAE,WAAW,YAAY,EAAE,WAAW;IAGtD,IAAI,aAFY,EAAE,WAAW,YAAY,EAAE,WAAW,WAGrD,OAAO,UAAU,KAAK;IAKvB,MAAM,QAAQ,IAAI,KAAK,EAAE,cAAc,EAAE,YAAY;IAGrD,OAAO,IAFW,KAAK,EAAE,cAAc,EAAE,YAE9B,CAAC,CAAC,QAAQ,IAAI,MAAM,QAAQ;GACxC,CAAC,EAAA,GAEsB;GACvB,IAAI,MAAM,MAAM,OAAO,KAAA;GAEvB,OAAO,MAAM,KAAK,cAAc,IAAI,UAAU,OAAO,OAAO,QAAQ,SAAS,KAAK;EACnF,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,OAAO,MAAM,EAAE;GACf;EACD;CACD;CAEA,MAUM,wBACL,UACA,OACA,OACA,MACA,KACA,SACA,UAGA,cACmC;EACnC,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,EAAE,aAAa,aAAa,+BAA+B,IAAI;EACrE,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,OACA,SACA,oBACA,OACA,YACD;EACA,IAAI;GACH,MAAM,iBAAiB,uBAAuB,WAAW;GACzD,MAAM,cAAc,uBAAuB,QAAQ;GAqBnD,MAAM,MAAK,MApBY,KAAK,QAC3B,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,GAAG,eAAe,0BAA0B,YAAY,gCAAgC,SAAS,cAClI;IACC,QAAQ;IACR,MAAM,KAAK,UAAU,EACpB,SAAS,CACR;KACC,OAAO,CAAC,GAAG;KACX,MAAM;IACP,CACD,EACD,CAAC;GACF,GACA,OACA,YACD,EAAA,EAEqB,QAAQ,EAAE,GAAG,IAAI,GAAG;GACzC,IAAI,MAAM,MAAM,OAAO,KAAA;GAEvB,MAAM,cAAc,MAAM,KAAK,QAC9B,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,GAAG,WAAW,QAAQ,EAAE,EAAE,0BAA0B,uBAAuB,GAAG,WAAW,EAAE,EAAE,gBAAgB,uBAAuB,GAAG,cAAc,SAAS,CAAC,KAC1N,EAAE,QAAQ,MAAM,GAChB,OACA,YACD;GACA,IAAI,eAAe,MAAM,OAAO,KAAA;GAEhC,OAAO,MAAM,KAAK,cAAc,aAAa,UAAU,OAAO,OAAO,SAAS,OAAO,YAAY;EAClG,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,OAAO,MAAM,EAAE;GACf;EACD;CACD;CAEA,MASa,eACZ,UACA,OACA,OACA,eACA,IACA,SACmC;EACnC,MAAM,QAAQ,gBAAgB;EAI9B,MAAM,cAAc,cAAc,MAAM,GAAG,CAAC,CAAC;EAE7C,IAAI;GACH,MAAM,KAAK,MAAM,KAAK,QACrB,UACA,OACA,QAAQ,SACR,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,WAAW,EAAE,0BAA0B,uBAAuB,EAAE,KAC3H,EACC,QAAQ,MACT,GACA,KACD;GAEA,OAAO,MAAM,OACV,MAAM,KAAK,cAAc,IAAI,UAAU,OAAO,OAAO,QAAQ,SAAS,KAAK,IAC3E,KAAA;EACJ,SAAS,IAAI;GAGZ,IAAI,GAAG,UAAU,WAAW,KAAK,OAAO,KAAA;GAExC,MAAM;EACP;CACD;CAEA,MASa,sBACZ,UACA,OACA,OACA,MACA,IACA,SAI0C;EAC1C,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,EAAE,aAAa,aAAa,+BAA+B,IAAI;EAErE,IAAI,SAAS,SAAS,KAAA,KAAa,SAAS,SAAS,SAAS;GAC7D,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,OACA,QAAQ,SACR,aACA,KACD;GACA,IAAI;GACJ,IAAI;IACH,cAAc,MAAM,KAAK,YACxB,UACA,OACA,OACA,aACA,IACA,QAAQ,SACR,UACA,KACD;GACD,SAAS,IAAI;IAGZ,IAAI,cAAc,qBAAqB,MAAM;IAE7C,IAAI,GAAG,UAAU,WAAW,KAAK;KAChC,OAAO,MAAM,EAAE;KACf;IACD;GACD;GAEA,IAAI,eAAe,MAAM;IACxB,MAAM,KAAK,uBACV,UACA,OACA,OACA,aACA,aACA,QAAQ,SACR,KACD;IACA,MAAM,YAAY,YAAY,OAAO;IACrC,MAAM,QAAQ,YAAY,OAAO;IACjC,MAAM,gBAAgB,MAAM,KAAK,yBAChC,WACA,OACA,UACA,OACA,OACA,aACA,OACD;IAEA,MAAM,kBAAkB,mCACvB,eACA,uBAAuB,YAAY,OAAO,mCAAmC,KAAK,KAAA,CACnF;IAEA,OAAO;KACN,IAAI,YAAY,GAAG,SAAS;KAC5B,MAAM;KACN,QAAQ,YAAY,GAAG,SAAS;KACtB;KACV,aAAa,IAAI,KAAK,YAAY,OAAO,qBAAqB;KAC9D,aAAa,IAAI,KAAK,YAAY,OAAO,qBAAqB;KAC9D,OAAO;KACP,QAAQ,oBAAoB;KAC5B,OAAO,YAAY,OAAO;KAC1B,KAAK,YAAY,OAAO,KAAK;IAC9B;GACD;EACD;EAEA,IAAI,SAAS,SAAS,KAAA,KAAa,SAAS,SAAS,eACpD,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,QAC3B,UACA,OACA,SAAS,SACT,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,WAAW,EAAE,0BAA0B,uBAAuB,QAAQ,EAAE,gBAAgB,uBAAuB,EAAE,KAC5K,EACC,QAAQ,MACT,GACA,KACD;GAEA,IAAI,YAAY,MACf,OAAO;IACN,IAAI,SAAS,cAAc,SAAS;IACpC,MAAM;IACN,QAAQ,SAAS,cAAc,SAAS;IAC9B;IACV,aAAa,IAAI,KAAK,SAAS,YAAY;IAC3C,aAAa,IAAI,KAAK,SAAS,YAAY;IAC3C,OAAO,8BAA8B,SAAS,MAAM;IACpD,QAAQ,+BAA+B,SAAS,MAAM;IACtD,OAAO,SAAS;IAChB,KAAK,0BAA0B,UAAU,QAAQ,SAAS,KAAK;GAChE;GAGD;EACD,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,IAAI,GAAG,UAAU,WAAW,KAAK;IAChC,OAAO,MAAM,EAAE;IACf;GACD;EACD;CAGF;CAEA,MAQa,SACZ,UACA,OACA,SACA,IACA,SAG6B;EAC7B,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,QAAQ,cACR,QAAQ,SACR,aACA,KACD;EAEA,IAAI;EACJ,IAAI;GACH,cAAc,MAAM,KAAK,YACxB,UACA,OACA,QAAQ,cACR,QAAQ,MACR,IACA,QAAQ,SACR,UACA,KACD;EACD,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,IAAI,GAAG,UAAU,WAAW,KAAK;IAChC,OAAO,MAAM,EAAE;IACf;GACD;EACD;EAEA,IAAI,eAAe,MAAM;GACxB,MAAM,KAAK,uBACV,UACA,OACA,QAAQ,cACR,QAAQ,MACR,aACA,QAAQ,SACR,KACD;GACA,MAAM,YAAY,YAAY,OAAO;GACrC,MAAM,QAAQ,YAAY,OAAO;GACjC,MAAM,CAAC,eAAe,cAAc,MAAM,QAAQ,IAAI,CACrD,KAAK,yBACJ,WACA,OACA,UACA,OACA,QAAQ,cACR,QAAQ,MACR,OACD,GACA,KAAK,sBACJ,UACA,OACA,QAAQ,cACR,QAAQ,SACR,YAAY,QACZ,KACD,CACD,CAAC;GACD,OAAO,kBAAkB,aAAa,UAAU,SAAS,eAAe,SAAS,UAAU;EAC5F;CAGD;CAEA,MAUM,oBACL,UACA,OACA,OACA,MACA,KACA,SACA,UAG0C;EAC1C,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,EAAE,aAAa,aAAa,+BAA+B,IAAI;EAErE,IAAI;GAYH,MAAM,UAAS,MAVM,KAAK,QACzB,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,WAAW,EAAE,0BAA0B,uBAAuB,QAAQ,EAAE,WAAW,uBAAuB,GAAG,KACxK,EACC,QAAQ,MACT,GACA,KACD,EAAA,EACuB;GACvB,IAAI,CAAC,QACJ;GAGD,OAAO;IACI;IACV,IAAI,KAAA;IACJ,UAAU,KAAA;IACV,MAAM,QAAQ;IACd,OAAO,QAAQ;IACf,WAAW,KAAA;GACZ;EACD,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,IAAI,GAAG,UAAU,WAAW,KAAK;IAChC,OAAO,MAAM,EAAE;IACf;GACD;EACD;CAGD;;;;;;;;;;CAWA,MAOM,uBACL,UACA,OACA,SACoD;EACpD,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,iBAAiB,MAAM,KAAK,QACjC,UACA,OACA,SACA,wBACA,EAAE,QAAQ,MAAM,GAChB,KACD;EACA,MAAM,UAAU,gBAAgB,iCAAiC,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO;EAC5F,MAAM,YAAY,IAAI,IAAI,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO;EACvE,IAAI,WAAW,QAAQ,aAAa,QAAQ,gBAAgB,cAAc,MAAM,OAAO,KAAA;EAEvF,MAAM,OAAO,kBAAkB,OAAO;EACtC,IAAI,KAAK,YAAY,MAAM,kBAAkB,SAAS,CAAC,CAAC,YAAY,GAAG,OAAO,KAAA;EAE9E,OAAO;GAAE,IAAI,eAAe;GAAkB;EAAK;CACpD;CAEA,MAOM,uBACL,UACA,OACA,SAC4G;EAC5G,MAAM,QAAQ,gBAAgB;EAE9B,IAAI;GA6BH,MAAM,QAAO,MA5BgB,KAAK,QAkBjC,UACA,OACA,SACA,wBACA,EACC,QAAQ,MACT,GACA,KACD,EAAA,EAE6B;GAC7B,MAAM,WAAW,MAAM,YAAY,SAAS;GAC5C,IAAI,CAAC,UACJ;GAGD,OAAO;IACN,IAAI,KAAK;IACT,MAAM,KAAK;IACX,OAAO,KAAK;IACF;GACX;EACD,SAAS,IAAI;GACZ,OAAO,MAAM,IAAI,mCAAmC,SAAS;GAC7D,MAAM;EACP;CACD;CAEA,MAAM,yBACL,WACA,OACA,UACA,OACA,OACA,aACA,SAGA,cACkD;EAClD,MAAM,SAAS,MAAM,KAAK,sBACzB,UACA,OACA,OACA,aACA,WACA,QAAQ,SACR,YACD;EACA,OAAO,kCAAkC,QAAQ,KAAK;CACvD;CAEA,sBACC,UACA,OACA,OACA,SACA,MACA,SACA,cACC;EACD,OAAO,KAAK,gBAAgB,UAC3B;GAAW;GAAS,WAAW;GAAgB;GAAS,OAAO,MAAM;GAAa,OAAO;EAAK,GAC9F,MACA,OAAM,WAAU;GACf,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,OACA,SACA,sBACA,iBACA,MACD;GACA,OAAO,KAAK,QACX,UACA,OACA,SACA,+BAA+B,OAAO,SAAS,MAAM,SAAS,UAAU,GACxE,EAAE,QAAQ,MAAM,GAChB,KAAA,GACA,MACD;EACD,GACA,cACA,OAAM,WAAU;GACf,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,OACA,SACA,wBACA,iBACA,MACD;GACA,MAAM,OAAO,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,OAAO,EAAE,+CAA+C,SAAS;GACzI,OAAO,KAAK,QAAQ,UAAU,OAAO,SAAS,MAAM,EAAE,QAAQ,MAAM,GAAG,KAAA,GAAW,MAAM;EACzF,CACD;CACD;CAEA,MAQM,sBACL,UACA,OACA,OACA,MACA,SAGA,cAC0C;EAC1C,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,OACA,QAAQ,SACR,gBACA,OACA,YACD;EAEA,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,cAC3B,UACA,OACA,OACA,MACA,QAAQ,SACR,SAAS,YACT,OACA,YACD;GACA,IAAI,YAAY,MAAM,OAAO,KAAA;GAK7B,OAAO;IACI;IACH;IAEP,MAAM,SAAS;IACf,QAAQ,SAAS,UAAU;IAC3B,QACC,SAAS,UAAU,SAAS,kBAAkB,QAAQ,OACnD;KAAS;KAAO,MAAM,SAAS,iBAAiB;IAAK,IACrD,KAAA;GACL;EACD,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAG7C,IAAI,CAAC,oBAAoB,EAAE,KAAK,EAAE,cAAc,uBAC/C,OAAO,MAAM,EAAE;GAEhB;EACD;CACD;CAEA,MAQM,iBACL,UACA,OACA,OACA,MACA,SAGA,cACqC;EACrC,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,OACA,QAAQ,SACR,gBACA,OACA,YACD;EAEA,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,cAC3B,UACA,OACA,OACA,MACA,QAAQ,SACR,SAAS,YACT,OACA,YACD;GACA,IAAI,UAAU,iBAAiB,MAAM,OAAO,KAAA;GAE5C,OAAO;IACI;IACV,MAAM,yBAAyB,SAAS,aAAa;GACtD;EACD,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAG7C,IAAI,CAAC,oBAAoB,EAAE,KAAK,EAAE,cAAc,uBAC/C,OAAO,MAAM,EAAE;GAEhB;EACD;CACD;;;;;;;;;;;;;;;;;;;;CAqBA,MAOM,oBACL,UACA,OACA,YACA,SAKA,cACmC;EACnC,MAAM,QAAQ,gBAAgB;EAC9B,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;EAEvD,MAAM,WAAW,KAAK,UAAU,CAC/B,QAAQ,UAAU,SAAS,QAC3B,0BACC;GAAE,GAAG,QAAQ;GAAU,OAAO;EAAM,GACpC,mBAAmB,QAAQ,UAAU,IAAI,GACzC,QAAQ,YACT,CACD,CAAC;EACD,MAAM,MAAM,+BAA+B,YAAY,QAAQ;EAC/D,MAAM,WAAW,sBAAsB,QAAQ,UAAU,KAAA,GAA4B;EACrF,MAAM,SAAS,+BAA+B,QAAQ,QAAQ,KAAK,QAAQ;EAC3E,MAAM,OAAO,QAAQ,QAAQ;EAI7B,MAAM,WAAW,QAAQ,YAAY,KAAK;EAC1C,MAAM,cAAc,KAAK,UAAU;GAClC,MAAM,OAAO,MAAM,WAAW;GAC9B,QAAQ;GACR;GACA;GACA;EACD,CAAC;EAED,MAAM,gBAAgB,QAAQ,YAAY,OAAO,KAAK,kBAAkB,IAAI,WAAW,IAAI,KAAA;EAC3F,IAAI,QAAQ,YAAY,QAAQ,iBAAiB,MAChD,MAAM,IAAI,MAAM,qEAAqE;EAGtF,MAAM,SAAS,OAAO,iBACrB,KAAK,kBAAkB,YACtB,cACC,YAAY,WACZ,KAAK,mBAAmB,UAAU,OAAO,YAAY,SAAS,MAAM,OAAO,MAAM,CAAC,CAAC,OACjF,OAAgB;GAChB,IAAI,uBAAuB,EAAE,GAC5B,MAAM,IAAI,MACT,GAAG,SAAS,KAAK,gFAClB;GAGD,MAAM;EACP,CACD,GACD,EAAgB,aAAa,CAC9B;EACD,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;EAEvD,MAAM,eAAe,OAAO,SAAS;EACrC,MAAM,MAAM,eAAe,OAAO,MAAM,GAAG,8BAA8B,IAAI;EAE7E,MAAM,SAAS,QAAQ,UAAU;EAEjC,MAAM,QAAQ,IAAI,MAAM,QAAQ,SAAS,QAAQ;EACjD,MAAM,SAAkB,CAAC;EACzB,IAAI,MAAM,SAAS,GAAG;GACrB,MAAM,QAAQ,MAAM,KAAK,gBACxB,UACA,OACA,YACA,QAAQ,SACR,kBACA,OACA,YACD;GACA,MAAM,WACL,SACC,MAAM,KAAK,oBACX,UACA,OACA,YACA,QAAQ,SACR,aACA,OACA,YACD;GACD,MAAM,QACL,SAAS,OACN,8BAA8B,SAAS,eACvC,iBAAiB,MAAM,KAAK,GAAG,EAAE,8CAA8C,SAAS;GAC5F,MAAM,YAAY,MAAM,KAAK,QAC5B,UACA,OACA,QAAQ,SACR,GAAG,uBAAuB,UAAU,EAAE,aAAa,SACnD,SAAS,OACN;IAAE,QAAQ;IAAQ,MAAM,KAAK,UAAU;KAAE,KAAK;KAAO,SAAS;KAAS,aAAa;IAAO,CAAC;GAAE,IAC9F,EAAE,QAAQ,MAAM,GACnB,OACA,YACD;GACA,MAAM,OAAO,IAAI,KAAK,WAAW,SAAS,CAAC,EAAA,CAAG,QAAO,MAAK,KAAK,IAAI,CAAC,CAAC,KAAI,MAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;GACxF,KAAK,MAAM,MAAM,OAAO;IAGvB,MAAM,WAAW,KAAK,IAAI,EAAE;IAC5B,IAAI,YAAY,MAAM;IAEtB,MAAM,cAAc,SAAS,OAAO;IACpC,IACC,QAAQ,cAAc,UACtB,CAAC,QAAQ,aAAa,MAAK,SAAQ,KAAK,YAAY,MAAM,aAAa,YAAY,CAAC,GAEpF,MAAM,IAAI,MAAM,0BAA0B,GAAG,mCAAmC;IAGjF,MAAM,UAAU,QAAQ,eAAe,WAAW;IAClD,IAAI,WAAW,MACd,MAAM,IAAI,MAAM,0BAA0B,GAAG,kBAAkB,YAAY,uBAAuB;IAGnG,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;IAEvD,MAAM,gBAAgB,MAAM,KAAK,yBAChC,SAAS,OAAO,wBAChB,SAAS,OAAO,iBAChB,UACA,OACA,YACA,QAAQ,MACR,SACA,YACD;IACA,MAAM,aAAa,MAAM,KAAK,sBAC7B,UACA,OACA,YACA,QAAQ,SACR,SAAS,QACT,OACA,YACD;IACA,OAAO,KAAK,kBAAkB,UAAU,UAAU,SAAS,eAAe,UAAU,UAAU,CAAC;GAChG;EACD;EAEA,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;EAEvD,MAAM,aAAa,SAAS,MAAM;EAClC,MAAM,UAAU,aAAa,IAAI;EACjC,MAAM,OAAkC;GAAO;GAAe;GAAU,QAAQ;GAAY,MAAM,OAAO;EAAE;EAC3G,OAAO;GACE;GACR,QAAQ,UAAU,KAAK,UAAU,IAAI,IAAI,KAAA;GAChC;GACH;GACN,WAAW;GACX,YAAY,eAAe,KAAA,IAAY,IAAI;GAC3C,cAAc,gBAAgB,KAAA;EAC/B;CACD;;;;;;;CAQA,MAOM,eACL,UACA,OACA,YACA,SACA,cACoC;EACpC,MAAM,QAAQ,gBAAgB;EAC9B,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;EAEvD,IAAI;GACH,MAAM,SAAS,MAAM,KAAK,mBACzB,UACA,OACA,YACA,SACA,OACA,OACA,YACD;GACA,OAAO,OAAO,SAAA,MAA0C,kBAAkB,OAAO;EAClF,SAAS,IAAI;GACZ,IAAI,uBAAuB,EAAE,GAAG,OAAO;GAEvC,MAAM;EACP;CACD;CAEA,MAAc,mBACb,UACA,OACA,YACA,SACA,SACA,OACA,cACoB;EACpB,MAAM,UAAU,MAAM,KAAK,yBAAyB,UAAU,OAAO,YAAY,SAAS,YAAY;EACtG,OAAO,yBACN,SACA,mBAAmB,QAAQ,UAAU,IAAI,GACzC,UACC,MAAM,KAAK,SACX,KAAK,iBACJ,UACA,OACA,YACA,QAAQ,SACR,MACA,KACA,OACA,cACA,IACD,GACD,YACD;CACD;CAEA,MAAc,yBACb,UACA,OACA,YACA,SACA,cACoB;EACpB,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;EAEvD,MAAM,eAAe,QAAQ,gBAAgB,QAAQ,wBAAwB,CAAC;EAC9E,IAAI;EACJ,IAAI,QAAQ,UAAU,UAAU,OAAO;GACtC,MAAM,SACL,MAAM,WAAW,cAAc,6BAA6B,OAAM,YAAW;IAC5E,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;IAEvD,MAAM,QAAQ;KACb,SAAS,QAAQ;KACjB,WAAW;KACF;KACT,OAAO,MAAM;KACb,OAAO;IACR;IAyBA,QAAO,MAxBa,KAAK,gBAAgB,SACxC,OACA,OAAM,WAAU;KACf,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,YACA,QAAQ,SACR,iBACA,OACA,MACD;KACA,OAAO,KAAK,QACX,UACA,OACA,KAAA,GACA,yBAAyB,QAAQ,SAAS,YAAY,SAAS,SAAS,UAAU,GAClF,EAAE,QAAQ,MAAM,GAChB,KAAA,GACA,MACD;IACD,GACA,YACD,EAAA,CACa,KAAI,UAAS;KAAW;KAAe;KAAa;IAAM,EAAE;GAC1E,CAAC,EAAA,CACA,KAAK;GACP,MAAM,SAAS,MAAM,WAAW,OAAO,6BAA6B,OAAM,UAAS;IAClF,MAAM,KAAK;IACX,SAAS,KAAK;IACd,QAAQ,oCACP,MAAM,KAAK,sBACV,UACA,OACA,YACA,KAAK,SACL,KAAK,MACL,QAAQ,SACR,YACD,CACD;GACD,EAAE;GACF,YAAY,IAAI,IACf,aAAa,KAAI,YAAW,CAAC,SAAS,EAAE,OAAO,OAAO,QAAO,SAAQ,KAAK,YAAY,OAAO,EAAE,CAAC,CAAC,CAClG;EACD;EAEA,OAAO,6BACN,QAAQ,UACR,mBAAmB,QAAQ,UAAU,IAAI,GACzC,cACA,SACD;CACD;;;;;;CAOA,MAAc,iBACb,UACA,OACA,YACA,SACA,MACA,KACA,OACA,cACA,MAC4C;EAC5C,MAAM,QAAQ,QAAQ,OAAO,OAAO,GAAG,KAAK,SAAS,KAAK;EAC1D,IAAI,MAAM,SAAA,MACT,MAAM,IAAI,MAAM,oEAAoE;EAGrF,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,YACA,SACA,QACA,OACA,YACD;EACA,MAAM,SAAS,MAAM,KAAK,QACzB,UACA,OACA,SACA,GAAG,uBAAuB,UAAU,EAAE,uBAAuB,IAAI,kCAAkC,SAAS,cAC5G;GAAE,QAAQ;GAAQ,MAAM,KAAK,UAAU,EAAS,MAAM,CAAC;EAAE,GACzD,OACA,YACD;EACA,IAAI,QAAQ,aAAa,MAAM,MAAM,IAAI,MAAM,iDAAiD;EAEhG,OAAO;GAAE,KAAK,OAAO,UAAU,KAAI,MAAK,EAAE,EAAE;GAAG,MAAM,OAAO;EAAK;CAClE;CAEA,MAAc,cACb,UACA,OACA,OACA,MACA,SACA,YACA,OACA,cACmD;EACnD,MAAM,EAAE,aAAa,aAAa,+BAA+B,IAAI;EACrE,MAAM,iBAAiB,uBAAuB,WAAW;EACzD,MAAM,cAAc,uBAAuB,QAAQ;EACnD,OAAO,KAAK,QACX,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,GAAG,eAAe,0BAA0B,YAAY,eAAe,cACxG,EAAE,QAAQ,MAAM,GAChB,OACA,YACD;CACD;CAEA,MAAc,cACb,IACA,UACA,OACA,OACA,SACA,OACA,cACuB;EACvB,MAAM,qBAAqB,MAAM,KAAK,sBACrC,UACA,OACA,OACA,SACA,GAAG,YAAY,YACf,OACA,YACD;EAEA,OAAO,qBAAqB,IAAI,UAAU,OAAO,SAAS,oBAAoB,OAAO;CACtF;;;;;;;;;;CAWA,MAAc,sBACb,UACA,OACA,OACA,SACA,YACA,OACA,cAC+C;EAC/C,IAAI,cAAc,MAAM,OAAO,KAAA;EAE/B,IAAI;EACJ,IAAI;GACH,YAAY,MAAM,OAAO,MAAM,WAAW;EAC3C,QAAQ;GACP;EACD;EAEA,MAAM,WAAW,KAAK,UAAU;GAAC;GAAW;GAAS;GAAO,WAAW;EAAE,CAAC;EAG1E,OAAO,KAAK,oBAAoB,YAC/B,WACC,WAAW,cACX,KAAK,wBACJ,UACA,OACA,OACA,SACA,WAAW,IACX,WACA,OACA,SACD,GACD,EAAgB,aAAa,CAC9B;CACD;CAEA,MAAc,wBACb,UACA,OACA,OACA,SACA,cACA,WACA,OACA,cAC+C;EAC/C,IAAI;GACH,uBAAuB,YAAY;GACnC,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,OACA,OACA,SACA,gBACA,OACA,YACD;GACA,MAAM,WAAW,MAAM,KAAK,QAC3B,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,0BAA0B,uBAAuB,YAAY,EAAE,eAAe,SAAS,cACxH,EAAE,QAAQ,MAAM,GAChB,OACA,YACD;GACA,MAAM,SAAS,2BAA2B,UAAU,QAAQ,SAAS,KAAK;GAC1E,MAAM,aAAa,2BAA2B,UAAU,WAAW,SAAS,KAAK;GAIjF,MAAM,MAAM,UAAU;GACtB,IAAI,OAAO,MAAM;IAChB,UAAU,WAAW;IACrB;GACD;GAEA,OAAO;IAAO;IAAiB;GAAW;EAC3C,SAAS,IAAI;GAEZ,IAAI,EAAE,cAAc,yBAAyB,EAAE,cAAc,sBAC5D,UAAU,WAAW;GAGtB,MAAM,SAAS,cAAc,qBAAqB,KAAK,GAAG,OAAO,KAAK;GACtE,OAAO,KAAK,6CAA6C,QAAQ;GACjE;EACD;CACD;CAEA,MAAc,YACb,UACA,OACA,OACA,SACA,IACA,SACA,UACA,OACgC;EAChC,MAAM,iBAAiB,uBAAuB,OAAO;EACrD,MAAM,YAAY,uBAAuB,EAAE;EAC3C,MAAM,cAAc,SAAS,gBAAgB,IAAI,mBAAmB;EACpE,OAAO,KAAK,QACX,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,IAAI,YAAY,uBAAuB,UAAU,eAAe,SAAS,cACxG,EAAE,QAAQ,MAAM,GAChB,KACD;CACD;CAEA,MAAc,uBACb,UACA,OACA,OACA,SACA,UACA,SACA,OACgB;EAChB,MAAM,gBAAgB,SAAS,SAAS;EACxC,IAAI,OAAO,kBAAkB,YAAY,CAAC,cAAc,KAAK,GAC5D,MAAM,IAAI,MAAM,0BAA0B,SAAS,GAAG,gBAAgB;EAGvE,IAAI,cAAc,YAAY,MAAM,QAAQ,YAAY,GAAG;EAU3D,KAAI,MARoB,KAAK,QAC5B,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,kBAAkB,uBAAuB,OAAO,KACjF,EAAE,QAAQ,MAAM,GAChB,KACD,EAAA,EACe,MAAM,YAAY,MAAM,cAAc,YAAY,GAChE,MAAM,IAAI,MAAM,0BAA0B,SAAS,GAAG,kCAAkC;CAE1F;CAEA,MAAc,sBACb,UACA,OACA,WACA,SACA,QACA,OACA,cAC6C;EAC7C,IAAI;GACH,MAAM,eAAe,EAAE,GAAG,MAAM;GAChC,OAAO,MAAM,KAAK,sBAAsB,UACvC,QACA;IAAW;IAAoB;IAAW,OAAO,aAAa;IAAa,OAAO;GAAK,GACvF,OAAM,WAAU;IACf,MAAM,WAAW,MAAM,KAAK,oBAC3B,UACA,cACA,WACA,SACA,UACA,OACA,MACD;IACA,OAAO,KAAK,QACX,UACA,cACA,SACA,GAAG,uBAAuB,SAAS,EAAE,gCAAgC,SAAS,cAC9E,EAAE,QAAQ,MAAM,GAChB,KAAA,GACA,MACD;GACD,GACA,YACD;EACD,SAAS,IAAI;GACZ,IAAI,oBAAoB,EAAE,KAAK,cAAc,SAAS,MAAM;GAE5D,OAAO,KAAK,qDAAqD,UAAU,KAAK,IAAI;GACpF,OAAO,CAAC;EACT;CACD;CAEA,MAAc,gBACb,UACA,OACA,WACA,SACA,UACA,SACA,cACmD;EACnD,IAAI;GACH,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;GAEvD,uBAAuB,SAAS;GAChC,MAAM,YAAY,KAAK,OAAO,yBAAyB,SAAS,mBAAmB,SAClF,KAAK,QAAQ,mBACZ;IAAa;IAAqB;IAAmB;GAAQ,GAC7D;IAAE,OAAO,MAAM;IAAa,OAAO;IAAe;GAAQ,CAC3D,CACD;GACA,IAAI,cAAc,SAEjB,UAAe,YAAY,CAAC,CAAC;GAG9B,OAAO,OAAO,gBAAgB,OAAO,eAAe,WAAW,YAAY,IAAI;EAChF,SAAS,IAAI;GACZ,OAAO,mBAAmB,OAAO,EAAE;EACpC;CACD;CAEA,MAAc,oBACb,UACA,OACA,WACA,SACA,UACA,SACA,cACuC;EACvC,IAAI;EACJ,IAAI;GACH,SAAS,MAAM,KAAK,gBAAgB,UAAU,OAAO,WAAW,SAAS,UAAU,SAAS,YAAY;EACzG,SAAS,IAAI;GACZ,IAAI,cAAc,sBACjB,MAAM,IAAI,MAAM,+CAA+C,EAAE,OAAO,GAAG,CAAC;GAG7E,MAAM;EACP;EACA,IAAI,UAAU,MAAM,MAAM,IAAI,MAAM,8BAA8B,SAAS,gBAAgB;EAE3F,OAAO;CACR;CAEA,MAAc,QACb,UACA,OACA,SACA,OACA,SACA,OACA,cACyB;EACzB,MAAM,EAAE,gBAAgB;EACxB,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU;EAE9C,IAAI;EACJ,IAAI;GACH,MAAM,KAAK,eAAe,WAAW,SAAS,UAAU,MAAM,WAAW,EAAE,KAAK,EAAE,UAAU,KAAK,EAAE,CAAC;GAEpG,IAAI;IACH,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;IAEvD,MAAM,MAAM,KAAK,OAAO,yBAAyB,SAAS,mBAAmB,SAC5E,KAAK,OAAO,MAAM,KAAK;KACtB,SAAS;MACR,eAAe,SAAS,OAAO,OAAO,aAAa;MACnD,gBAAgB;KACjB;KACA,QAAQ;KACR,GAAG;IACJ,CAAC,CACF;IAEA,IAAI,IAAI,IAAI;KAMX,KADoB,IAAI,QAAQ,IAAI,cAAc,CAAC,EAAE,YAAY,KAAK,GAAA,CACtD,WAAW,WAAW,GAAG;MACxC,MAAM,EAAE,aAAa,cAAc,GAAG,cAAc;MACpD,MAAM,IAAI,oBACT,WAAA,mBAEA,IAAI,MAAM,IAAI,IAAI,OAAO,uDAAuD,CACjF;KACD;KAEA,OAAQ,MAAM,IAAI,KAAK;IACxB;IAGA,MAAM,MAAM,mBAAmB,aAAa,eAAe,GAAG;GAC/D,UAAU;IACT,IAAI,KAAK;GACV;EACD,SAAS,IAAI;GACZ,IAAI,cAAc,sBAAsB,GAAG,SAAS,cACnD,KAAK,mBAAmB,UAAU,OAAO,IAAI,KAAK;QAC5C,IAAIA,QAAO,aACjB,KAAK,OAAO,UAAU,+BAA+B,GAAG,SAAS;GAGlE,MAAM;EACP;CACD;CAEA,mBACC,UACA,OACA,IACA,OACO;EACP,IAAI,GAAG,SAAS,gBAAgB,EAAE,cAAc,qBAAqB,MAAM,IAAI,kBAAkB,EAAE;EAEnG,MAAM,EAAE,aAAa,GAAG,cAAc;EACtC,QAAQ,GAAG,QAAX;GACC,KAAK;GACL,KAAK;GACL,KAAK,KACJ,MAAM,IAAI,qBAAqB,EAAE;GAClC,KAAK,KACJ,MAAM,IAAI,oBAAoB,WAAA,GAAmD,EAAE;GACpF,KAAK,KACJ,MAAM,iBAAiB,IAAI,WAAW;GACvC,KAAK;IAKJ,IAAI,oBAAoB,EAAE,GAAG,MAAM,iBAAiB,IAAI,WAAW;IAEnE,MAAM,IAAI,oBAAoB,WAAA,GAAgD,EAAE;GACjF,KAAK;IACJ,OAAO,MAAM,EAAE;IACf,IAAI,GAAG,YAAY,MAClB,KAAK,OAAO,kBACX,YAAY,QAAQ,SAAS,OAAO,UACjC,KAAK,EACL,0JACA,UAAU,QAAQ,aACnB,IACC,KAAK,EAAE,2DAA2D,SAAS,IAAI,CACnF;IAED;GACD,KAAK;IACJ,OAAO,MAAM,EAAE;IAOf;GACD,SACC,IAAI,GAAG,UAAU,OAAO,GAAG,SAAS,KAAK,MAAM,IAAI,mBAAmB,EAAE;EAE1E;EAEA,OAAO,MAAM,EAAE;EACf,IAAIA,QAAO,aACV,KAAK,OAAO,UACX,+BAAgC,GAAG,UAAkB,SAAS,EAAE,EAAE,WAAW,GAAG,SACjF;CAEF;AACD;AAxjDE,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,OAAO,MAAM,YAAY;CAChD,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CAC1B;CACD;CACE;AACT,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,2BAAA,IAAA;AA0DA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,OAAO,MAAM,KAAK,aAAa;CACtD,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CAC1B;CACD;CACD;CACI;AACV,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,2BAAA,IAAA;AAwEA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,OAAO,eAAe,QAAQ;CACrD,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CAC1B;CACQ;CACX;AACL,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,kBAAA,IAAA;AAuCA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,OAAO,MAAM,QAAQ;CAC5C,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CAC1B;CACD;CACF;AACL,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,yBAAA,IAAA;AAoIA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,SAAS,QAAQ;CACxC,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CACxB;CACL;AACL,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,YAAA,IAAA;AAgFA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,OAAO,MAAM,KAAK,aAAa;CACtD,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CAC1B;CACD;CACD;CACI;AACV,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,uBAAA,IAAA;AA+DA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,aAAa;CACpC,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CACxB;AACV,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,0BAAA,IAAA;AAyBA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,aAAa;CACpC,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CACxB;AACV,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,0BAAA,IAAA;AAgIA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,OAAO,UAAU;CACxC,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CAC1B;CACD;AACP,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,yBAAA,IAAA;AA8DA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,OAAO,UAAU;CACxC,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CAC1B;CACD;AACP,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,oBAAA,IAAA;AAuEA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,gBAAgB;CACvC,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CACrB;AACb,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,uBAAA,IAAA;AA6KA,WAAA,CAAA,MAAM,EACN,OAAO,UAAU,OAAO,gBAAgB;CACvC,UAAU,SAAS;CACnB,OAAO,UAAU,MAAM,UAAU;CACrB;AACb,GACD,CAAC,CAAA,GAAA,eAAA,WAAA,kBAAA,IAAA;;AA6lBF,SAAgB,qBACf,KACA,gBACA,sBACiB;CACjB,OAAO,IAAI,eAAe,sBAAsB,GAAG,GAAG,gBAAgB,oBAAoB;AAC3F"}
@@ -99,16 +99,15 @@ export interface WorkItem {
99
99
  'System.IterationPath'?: string;
100
100
  'System.WorkItemType': string;
101
101
  'System.State': string;
102
- 'System.AssignedTo': AzureUser;
102
+ 'System.AssignedTo': AzureUser | string;
103
103
  'System.CreatedDate': string;
104
- 'System.CreatedBy': AzureUser;
104
+ 'System.CreatedBy': AzureUser | string;
105
105
  'System.ChangedDate': string;
106
106
  'System.ChangedBy': AzureUser;
107
107
  'System.CommentCount': number;
108
- 'System.Description'?: string;
108
+ 'System.Description': string;
109
109
  'System.Title': string;
110
110
  'Microsoft.VSTS.Common.ClosedDate': string;
111
- [field: string]: unknown;
112
111
  };
113
112
  id: number;
114
113
  rev: number;
@@ -375,12 +374,7 @@ export interface AzureWorkItemEtagRead {
375
374
  * the SDK's row, the state carries no workflow category: `ProvidersApi` classifies both reads' rows the same way
376
375
  * afterwards, from the work item type's states.
377
376
  */
378
- export declare function fromAzureWorkItemToProviderIssue(workItem: AzureWorkItemResponse, namespace: string, project: string): ProviderIssue | undefined;
379
- /**
380
- * A work item's `description` and `descriptionField`, as provider-apis' list rows carry them: `descriptionField` only
381
- * when there is a description. A catalog only adds fields to try, so this pair read with `htmlFields` can replace a
382
- * row's pair read without them.
383
- */
377
+ export declare function fromAzureWorkItemToProviderIssue(workItem: AzureWorkItemResponse, namespace: string, project: string, htmlFields?: readonly AzureWorkItemHtmlField[]): ProviderIssue | undefined;
384
378
  export declare function toAzureWorkItemDescriptionFields(fields: Readonly<Record<string, unknown>> | null | undefined, htmlFields?: readonly AzureWorkItemHtmlField[]): Pick<ProviderIssue, 'description' | 'descriptionField'>;
385
379
  /** Only a UTC ISO timestamp naming a real calendar date and time, as provider-apis accepts; anything else is `null`. */
386
380
  export declare function parseAzureWorkItemDate(value: unknown): Date | null;
@@ -239,12 +239,21 @@ function fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls,
239
239
  resourceName: owner
240
240
  }, void 0, void 0, void 0, void 0, void 0, void 0, void 0, projection);
241
241
  }
242
+ function fromAzureWorkItemUserToMember(user) {
243
+ if (user == null || typeof user === "string" && !user.trim()) return void 0;
244
+ return typeof user === "string" ? {
245
+ id: user,
246
+ name: user,
247
+ username: user
248
+ } : fromAzureUserToMember(user, "issue");
249
+ }
242
250
  function fromAzureWorkItem(workItem, provider, project, stateCategory, projection, htmlFields) {
243
251
  const state = workItem.fields["System.State"];
244
252
  const description = getAzureWorkItemDescription(workItem.fields, htmlFields);
245
253
  const closedDate = parseAzureWorkItemDate(workItem.fields["Microsoft.VSTS.Common.ClosedDate"]) ?? void 0;
246
254
  const normalizedState = azureWorkItemsStateCategoryToState(stateCategory, closedDate);
247
- return new Issue$1(provider, workItem.id.toString(), workItem.id.toString(), workItem.fields["System.Title"], workItem._links.html.href, new Date(workItem.fields["System.CreatedDate"]), new Date(workItem.fields["System.ChangedDate"]), normalizedState === "closed", normalizedState, fromAzureUserToMember(workItem.fields["System.CreatedBy"], "issue"), workItem.fields["System.AssignedTo"] != null ? [fromAzureUserToMember(workItem.fields["System.AssignedTo"], "issue")] : [], void 0, closedDate, void 0, workItem.fields["System.CommentCount"], void 0, description?.value, project, void 0, workItem.fields["System.WorkItemType"], typeof state === "string" && state ? {
255
+ const assignee = fromAzureWorkItemUserToMember(workItem.fields["System.AssignedTo"]);
256
+ return new Issue$1(provider, workItem.id.toString(), workItem.id.toString(), workItem.fields["System.Title"], workItem._links.html.href, new Date(workItem.fields["System.CreatedDate"]), new Date(workItem.fields["System.ChangedDate"]), normalizedState === "closed", normalizedState, fromAzureWorkItemUserToMember(workItem.fields["System.CreatedBy"]), assignee != null ? [assignee] : [], void 0, closedDate, void 0, workItem.fields["System.CommentCount"], void 0, description?.value, project, void 0, workItem.fields["System.WorkItemType"], typeof state === "string" && state ? {
248
257
  name: state,
249
258
  category: azureIssueStateCategory(stateCategory)
250
259
  } : void 0, void 0, toWorkItemIterations(workItem.fields["System.IterationPath"]), projection, description != null ? toIssueBodyField(description.field) : void 0);
@@ -279,7 +288,7 @@ function toWorkItemIterations(path) {
279
288
  * the SDK's row, the state carries no workflow category: `ProvidersApi` classifies both reads' rows the same way
280
289
  * afterwards, from the work item type's states.
281
290
  */
282
- function fromAzureWorkItemToProviderIssue(workItem, namespace, project) {
291
+ function fromAzureWorkItemToProviderIssue(workItem, namespace, project, htmlFields) {
283
292
  const fields = workItem.fields;
284
293
  const id = workItem.id;
285
294
  const title = fields?.["System.Title"];
@@ -302,7 +311,7 @@ function fromAzureWorkItemToProviderIssue(workItem, namespace, project) {
302
311
  updatedDate: parseAzureWorkItemDate(fields["System.ChangedDate"]),
303
312
  url: typeof url === "string" ? url : null,
304
313
  assignees: assignee != null ? [assignee] : [],
305
- ...toAzureWorkItemDescriptionFields(fields),
314
+ ...toAzureWorkItemDescriptionFields(fields, htmlFields),
306
315
  state: typeof state === "string" && state ? {
307
316
  name: state,
308
317
  color: null
@@ -326,11 +335,6 @@ function fromAzureWorkItemToProviderIssue(workItem, namespace, project) {
326
335
  }))
327
336
  };
328
337
  }
329
- /**
330
- * A work item's `description` and `descriptionField`, as provider-apis' list rows carry them: `descriptionField` only
331
- * when there is a description. A catalog only adds fields to try, so this pair read with `htmlFields` can replace a
332
- * row's pair read without them.
333
- */
334
338
  function toAzureWorkItemDescriptionFields(fields, htmlFields) {
335
339
  const description = getAzureWorkItemDescription(fields, htmlFields);
336
340
  return {
@@ -340,6 +344,17 @@ function toAzureWorkItemDescriptionFields(fields, htmlFields) {
340
344
  }
341
345
  /** An identity field, as provider-apis maps it: `name` from `uniqueName`, `username` from `displayName`. */
342
346
  function fromAzureWorkItemIdentity(value) {
347
+ if (typeof value === "string") {
348
+ if (!value.trim()) return null;
349
+ return {
350
+ id: value,
351
+ name: value,
352
+ username: value,
353
+ email: null,
354
+ avatarUrl: null,
355
+ url: null
356
+ };
357
+ }
343
358
  const identity = value;
344
359
  if (typeof identity?.id !== "string" || !identity.id.trim()) return null;
345
360
  const avatarUrl = identity._links?.avatar?.href;
@@ -411,7 +426,8 @@ const azureWorkItemEtagFieldNames = [
411
426
  function toAzureWorkItemEtagFields(workItem, project, category) {
412
427
  const fields = workItem.fields;
413
428
  const teamProject = fields?.["System.TeamProject"];
414
- if (typeof teamProject === "string" && teamProject.toLowerCase() !== project.toLowerCase()) throw new Error(`Azure DevOps work item ${String(workItem.id)} is in project '${teamProject}', not '${project}'`);
429
+ if (typeof teamProject !== "string" || !teamProject.trim()) throw new Error(`Azure DevOps work item ${String(workItem.id)} has no project`);
430
+ if (teamProject.toLowerCase() !== project.toLowerCase()) throw new Error(`Azure DevOps work item ${String(workItem.id)} is in project '${teamProject}', not '${project}'`);
415
431
  const updatedDate = parseAzureWorkItemDate(fields?.["System.ChangedDate"]);
416
432
  if (updatedDate == null) throw new Error(`Azure DevOps returned work item ${String(workItem.id)} without a change date`);
417
433
  return {