@gitkraken/core-gitlens 0.9.1 → 0.9.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 (61) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/dist/git/providers/diff.d.ts +9 -0
  3. package/dist/git-cli/providers/diff.d.ts +7 -1
  4. package/dist/git-cli/providers/diff.js +28 -6
  5. package/dist/git-cli/providers/diff.js.map +1 -1
  6. package/dist/git-cli/providers/operations.d.ts +1 -0
  7. package/dist/git-cli/providers/operations.js +36 -22
  8. package/dist/git-cli/providers/operations.js.map +1 -1
  9. package/dist/plus/git-github/api/github.js +1 -1
  10. package/dist/plus/git-github/api/github.js.map +1 -1
  11. package/dist/plus/git-github/providers/github/diff.d.ts +3 -1
  12. package/dist/plus/git-github/providers/github/diff.js +2 -1
  13. package/dist/plus/git-github/providers/github/diff.js.map +1 -1
  14. package/dist/plus/integrations/integrationService.d.ts +2 -1
  15. package/dist/plus/integrations/integrationService.js +3 -1
  16. package/dist/plus/integrations/integrationService.js.map +1 -1
  17. package/dist/plus/integrations/models/integration.d.ts +8 -0
  18. package/dist/plus/integrations/models/integration.js +9 -0
  19. package/dist/plus/integrations/models/integration.js.map +1 -1
  20. package/dist/plus/integrations/providers/azure/azure.d.ts +3 -0
  21. package/dist/plus/integrations/providers/azure/azure.js +18 -0
  22. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  23. package/dist/plus/integrations/providers/azure/azure.utils.d.ts +12 -0
  24. package/dist/plus/integrations/providers/azure/azure.utils.js +46 -0
  25. package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -0
  26. package/dist/plus/integrations/providers/azure/models.d.ts +1 -0
  27. package/dist/plus/integrations/providers/azure/models.js +1 -1
  28. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  29. package/dist/plus/integrations/providers/azureDevOps.d.ts +3 -0
  30. package/dist/plus/integrations/providers/azureDevOps.js +7 -0
  31. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  32. package/dist/plus/integrations/providers/bitbucket/bitbucket.utils.d.ts +17 -0
  33. package/dist/plus/integrations/providers/bitbucket/bitbucket.utils.js +25 -0
  34. package/dist/plus/integrations/providers/bitbucket/bitbucket.utils.js.map +1 -0
  35. package/dist/plus/integrations/providers/bitbucket-server.d.ts +3 -0
  36. package/dist/plus/integrations/providers/bitbucket-server.js +7 -0
  37. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  38. package/dist/plus/integrations/providers/bitbucket.d.ts +3 -0
  39. package/dist/plus/integrations/providers/bitbucket.js +7 -0
  40. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  41. package/dist/plus/integrations/providers/jira-server.d.ts +3 -1
  42. package/dist/plus/integrations/providers/jira-server.js +19 -5
  43. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  44. package/dist/plus/integrations/providers/jira.d.ts +20 -3
  45. package/dist/plus/integrations/providers/jira.js +129 -50
  46. package/dist/plus/integrations/providers/jira.js.map +1 -1
  47. package/dist/plus/integrations/providers/linear.d.ts +13 -2
  48. package/dist/plus/integrations/providers/linear.js +41 -10
  49. package/dist/plus/integrations/providers/linear.js.map +1 -1
  50. package/dist/plus/integrations/providers/providerErrors.d.ts +11 -0
  51. package/dist/plus/integrations/providers/providerErrors.js +22 -1
  52. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  53. package/dist/plus/integrations/providers/utils/discoveryCache.d.ts +36 -0
  54. package/dist/plus/integrations/providers/utils/discoveryCache.js +59 -0
  55. package/dist/plus/integrations/providers/utils/discoveryCache.js.map +1 -0
  56. package/dist/plus/integrations/reads/drains.js +4 -4
  57. package/dist/plus/integrations/reads/drains.js.map +1 -1
  58. package/dist/plus/integrations/reads/warnings.d.ts +11 -0
  59. package/dist/plus/integrations/reads/warnings.js +14 -1
  60. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  61. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"azure.js","names":["Logger"],"sources":["../../../../../../plus/integrations/src/providers/azure/azure.ts"],"sourcesContent":["import * 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 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\tAzureWorkItemState,\n\tAzureWorkItemStateCategory,\n\tWorkItem,\n} from './models.js';\nimport {\n\tazurePullRequestStatusToState,\n\tazureWorkItemsStateCategoryToState,\n\tencodeAzurePathSegment,\n\tfromAzurePullRequest,\n\tfromAzureWorkItem,\n\tgetAzurePullRequestWebUrl,\n\tisClosedAzurePullRequestStatus,\n\tisClosedAzureWorkItemStateCategory,\n\tnormalizeAzureBranchName,\n\tsanitizeAzureRepositoryUrl,\n} from './models.js';\nimport type { AzureWorkItemSearchCursor } from './search.js';\nimport {\n\tazureWorkItemBatchLimit,\n\tazureWorkItemSearchResultLimit,\n\tparseAzureWorkItemSearchCursor,\n\ttoAzureSearchPageSize,\n\ttoAzureWorkItemSearchCursorKey,\n\ttoAzureWorkItemSearchWiql,\n} from './search.js';\n\nconst forkRepositoryUrlCacheTtl = 5 * 60 * 1000;\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\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\nclass WorkItemStates {\n\tprivate readonly _categories = new Map<string, AzureWorkItemStateCategory>();\n\tprivate readonly _types = new Map<string, AzureWorkItemState[]>();\n\n\t// TODO@sergeibbb: we might need some logic for invalidating\n\tpublic getStateCategory(\n\t\tproject: string,\n\t\tworkItemType: string,\n\t\tstateName: string,\n\t): AzureWorkItemStateCategory | undefined {\n\t\treturn this._categories.get(this.getStateKey(project, workItemType, stateName));\n\t}\n\n\tpublic clear(): void {\n\t\tthis._categories.clear();\n\t\tthis._types.clear();\n\t}\n\n\tpublic saveTypeStates(project: string, workItemType: string, states: AzureWorkItemState[]): void {\n\t\tthis.clearTypeStates(project, workItemType);\n\t\tthis._types.set(this.getTypeKey(project, workItemType), states);\n\t\tfor (const state of states) {\n\t\t\tthis._categories.set(this.getStateKey(project, workItemType, state.name), state.category);\n\t\t}\n\t}\n\n\tpublic hasTypeStates(project: string, workItemType: string): boolean {\n\t\treturn this._types.has(this.getTypeKey(project, workItemType));\n\t}\n\n\tprivate clearTypeStates(project: string, workItemType: string): void {\n\t\tconst states = this._types.get(this.getTypeKey(project, workItemType));\n\t\tif (states == null) return;\n\n\t\tfor (const state of states) {\n\t\t\tthis._categories.delete(this.getStateKey(project, workItemType, state.name));\n\t\t}\n\t}\n\n\tprivate getStateKey(project: string, workItemType: string, stateName: string): string {\n\t\t// By stringifying the pair as JSON we make sure that all possible special characters are escaped\n\t\treturn JSON.stringify([project, workItemType, stateName]);\n\t}\n\n\tprivate getTypeKey(project: string, workItemType: string): string {\n\t\treturn JSON.stringify([project, workItemType]);\n\t}\n}\n\nexport class AzureDevOpsApi implements Disposable {\n\tprivate readonly _disposable: Disposable | undefined;\n\tprivate _workItemStates: WorkItemStates = new WorkItemStates();\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(private readonly config: ProviderApiConfig) {\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._workItemStates.clear();\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, 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\ttry {\n\t\t\t\t// Try to get the Work item (wit) first with specific fields\n\t\t\t\tconst issueResult = 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\n\t\t\t\tif (issueResult != null) {\n\t\t\t\t\tconst issueType = issueResult.fields['System.WorkItemType'];\n\t\t\t\t\tconst state = issueResult.fields['System.State'];\n\t\t\t\t\tconst stateCategory = await this.getWorkItemStateCategory(\n\t\t\t\t\t\tissueType,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\ttoken,\n\t\t\t\t\t\towner,\n\t\t\t\t\t\tprojectName,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: issueResult.id.toString(),\n\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\tnodeId: issueResult.id.toString(),\n\t\t\t\t\t\tprovider: provider,\n\t\t\t\t\t\tcreatedDate: new Date(issueResult.fields['System.CreatedDate']),\n\t\t\t\t\t\tupdatedDate: new Date(issueResult.fields['System.ChangedDate']),\n\t\t\t\t\t\tstate: azureWorkItemsStateCategoryToState(stateCategory),\n\t\t\t\t\t\tclosed: isClosedAzureWorkItemStateCategory(stateCategory),\n\t\t\t\t\t\ttitle: issueResult.fields['System.Title'],\n\t\t\t\t\t\turl: issueResult._links.html.href,\n\t\t\t\t\t};\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\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\ttry {\n\t\t\t// Try to get the Work item (wit) first with specific fields\n\t\t\tconst issueResult = 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\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\tproject.resourceName,\n\t\t\t\t\tproject.name,\n\t\t\t\t\toptions,\n\t\t\t\t);\n\t\t\t\treturn fromAzureWorkItem(issueResult, provider, project, stateCategory);\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\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\t// A rejected credential is the whole point of this read failing; reporting \"no user\" instead hides it.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tscope?.error(ex, `Failed to get current user from ${baseUrl}`);\n\t\t\treturn undefined;\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): Promise<AzureWorkItemStateCategory | undefined> {\n\t\t// By installation too: this API is shared by every connection, and two installations can each have a\n\t\t// collection and project of the same name whose process maps a state to a different category.\n\t\tconst project = JSON.stringify([options.baseUrl, owner, projectName]);\n\t\tconst category = this._workItemStates.getStateCategory(project, issueType, state);\n\t\tif (category != null) return category;\n\n\t\tconst states = await this.retrieveWorkItemTypeStates(issueType, provider, token, owner, projectName, options);\n\t\tthis._workItemStates.saveTypeStates(project, issueType, states);\n\n\t\treturn this._workItemStates.getStateCategory(project, issueType, state);\n\t}\n\n\tprivate async retrieveWorkItemTypeStates(\n\t\tworkItemType: 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): Promise<AzureWorkItemState[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst issueResult = await this.request<{ value: AzureWorkItemState[]; count: number }>(\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/wit/workItemTypes/${encodeAzurePathSegment(workItemType)}/states`,\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 issueResult?.value ?? [];\n\t\t} catch (ex) {\n\t\t\t// A rejected credential must not be degraded to an empty state list: the caller caches whatever this\n\t\t\t// returns, so an empty list would pin every work item of this type to an unknown state until the\n\t\t\t// cache is dropped — long after the session recovery this error is supposed to trigger.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tscope?.error(ex);\n\t\t\treturn [];\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\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: a WIQL query for the ordered ids, then one detail\n\t * 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 it reads a consistent set rather than re-running a query whose rows move under\n\t * an `updated` order. Each first page queries its own snapshot, so paginations of one query never share one, and\n\t * a continuation whose snapshot is gone is refused (see {@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: {\n\t\t\tbaseUrl: string;\n\t\t\tcriteria?: IssueSearchCriteria;\n\t\t\tprojectNames?: readonly string[];\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\tconst wiql = toAzureWorkItemSearchWiql(\n\t\t\toptions.criteria,\n\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\toptions.projectNames,\n\t\t);\n\t\tconst key = toAzureWorkItemSearchCursorKey(collection, wiql);\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// Keyed by credential and address, and by the query itself rather than its fingerprint: two accounts on one\n\t\t// installation must never page through each other's matches, and two queries whose fingerprints collide must\n\t\t// never share a snapshot. The full credential digest, since the token's `microHash` is truncated for logs.\n\t\t// And by the snapshot's own id: a first page queries a new snapshot rather than replacing the query's, so a\n\t\t// pagination still reading an earlier one keeps it (see `AzureWorkItemSearchCursor`).\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\twiql,\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.queryWorkItemIds(\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\toptions.baseUrl,\n\t\t\t\t\t\twiql,\n\t\t\t\t\t\tazureWorkItemSearchResultLimit + 1,\n\t\t\t\t\t\tscope,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t).catch((ex: unknown) => {\n\t\t\t\t\t\t// `$top` is expected to bound the answer; should a server refuse the match set regardless, say\n\t\t\t\t\t\t// what happened rather than surfacing a bare client error.\n\t\t\t\t\t\tif (isWorkItemLimitRefusal(ex)) {\n\t\t\t\t\t\t\tthrow new Error(\n\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);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}),\n\t\t\t\t{ cancellation: cancellation },\n\t\t\t));\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\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\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);\n\t\t\t\tvalues.push(fromAzureWorkItem(workItem, provider, project, stateCategory));\n\t\t\t}\n\t\t}\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: { baseUrl: string; criteria?: IssueSearchCriteria; projectNames?: readonly string[] },\n\t\tcancellation?: AbortSignal,\n\t): Promise<number | 'exceeds-limit'> {\n\t\tconst scope = getScopedLogger();\n\t\tconst wiql = toAzureWorkItemSearchWiql(\n\t\t\toptions.criteria,\n\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\toptions.projectNames,\n\t\t);\n\t\ttry {\n\t\t\tconst ids = await this.queryWorkItemIds(\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\twiql,\n\t\t\t\tazureWorkItemSearchResultLimit + 1,\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\treturn ids.length > azureWorkItemSearchResultLimit ? 'exceeds-limit' : ids.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\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): Promise<number[]> {\n\t\tconst result = await this.request<{ workItems?: { id: number }[] }>(\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: wiql }) },\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 result.workItems.map(w => w.id);\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\treturn fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls);\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\tprovider?.trackRequestException();\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(ctx: IntegrationServiceContext): AzureDevOpsApi {\n\treturn new AzureDevOpsApi(baseProviderApiConfig(ctx));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsEA,MAAM,4BAA4B;;AAElC,MAAM,4BAA4B;AAClC,MAAM,+BAA+B;;AAGrC,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,IAAM,iBAAN,MAAqB;CACpB,8BAA+B,IAAI,IAAwC;CAC3E,yBAA0B,IAAI,IAAkC;CAGhE,iBACC,SACA,cACA,WACyC;EACzC,OAAO,KAAK,YAAY,IAAI,KAAK,YAAY,SAAS,cAAc,SAAS,CAAC;CAC/E;CAEA,QAAqB;EACpB,KAAK,YAAY,MAAM;EACvB,KAAK,OAAO,MAAM;CACnB;CAEA,eAAsB,SAAiB,cAAsB,QAAoC;EAChG,KAAK,gBAAgB,SAAS,YAAY;EAC1C,KAAK,OAAO,IAAI,KAAK,WAAW,SAAS,YAAY,GAAG,MAAM;EAC9D,KAAK,MAAM,SAAS,QACnB,KAAK,YAAY,IAAI,KAAK,YAAY,SAAS,cAAc,MAAM,IAAI,GAAG,MAAM,QAAQ;CAE1F;CAEA,cAAqB,SAAiB,cAA+B;EACpE,OAAO,KAAK,OAAO,IAAI,KAAK,WAAW,SAAS,YAAY,CAAC;CAC9D;CAEA,gBAAwB,SAAiB,cAA4B;EACpE,MAAM,SAAS,KAAK,OAAO,IAAI,KAAK,WAAW,SAAS,YAAY,CAAC;EACrE,IAAI,UAAU,MAAM;EAEpB,KAAK,MAAM,SAAS,QACnB,KAAK,YAAY,OAAO,KAAK,YAAY,SAAS,cAAc,MAAM,IAAI,CAAC;CAE7E;CAEA,YAAoB,SAAiB,cAAsB,WAA2B;EAErF,OAAO,KAAK,UAAU;GAAC;GAAS;GAAc;EAAS,CAAC;CACzD;CAEA,WAAmB,SAAiB,cAA8B;EACjE,OAAO,KAAK,UAAU,CAAC,SAAS,YAAY,CAAC;CAC9C;AACD;AAEA,IAAa,iBAAb,MAAkD;CAkBpB;CAjB7B;CACA,kBAA0C,IAAI,eAAe;CAC7D,sBAAuC,IAAI,aAA0D;EACpG,UAAU;EACV,WAAW;CACZ,CAAC;;;;;;CAMD,oBAAqC,IAAI,aAA+B;EACvE,UAAU;EACV,WAAW;EACX,WAAW;CACZ,CAAC;CAED,YAAY,QAA4C;EAA3B,KAAA,SAAA;EAC5B,KAAK,cAAc,OAAO,wBAAwB,KAAK,YAAY,CAAC;CACrE;CAEA,UAAgB;EACf,KAAK,aAAa,QAAQ;CAC3B;CAEA,cAA4B;EAC3B,KAAK,gBAAgB,MAAM;EAC3B,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,IAAI;GAqBH,MAAM,MAAK,MApBY,KAAK,QAC3B,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,WAAW,EAAE,0BAA0B,uBAAuB,QAAQ,EAAE,oCACnI;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,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,SACpD,IAAI;GAEH,MAAM,cAAc,MAAM,KAAK,QAC9B,UACA,OACA,SAAS,SACT,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,WAAW,EAAE,uBAAuB,uBAAuB,EAAE,KACxH,EACC,QAAQ,MACT,GACA,KACD;GAEA,IAAI,eAAe,MAAM;IACxB,MAAM,YAAY,YAAY,OAAO;IACrC,MAAM,QAAQ,YAAY,OAAO;IACjC,MAAM,gBAAgB,MAAM,KAAK,yBAChC,WACA,OACA,UACA,OACA,OACA,aACA,OACD;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,mCAAmC,aAAa;KACvD,QAAQ,mCAAmC,aAAa;KACxD,OAAO,YAAY,OAAO;KAC1B,KAAK,YAAY,OAAO,KAAK;IAC9B;GACD;EACD,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,IAAI,GAAG,UAAU,WAAW,KAAK;IAChC,OAAO,MAAM,EAAE;IACf;GACD;EACD;EAGD,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;EAE9B,IAAI;GAEH,MAAM,cAAc,MAAM,KAAK,QAC9B,UACA,OACA,SAAS,SACT,GAAG,uBAAuB,QAAQ,YAAY,EAAE,GAAG,uBAAuB,QAAQ,IAAI,EAAE,uBAAuB,uBAAuB,EAAE,KACxI,EACC,QAAQ,MACT,GACA,KACD;GAEA,IAAI,eAAe,MAAM;IACxB,MAAM,YAAY,YAAY,OAAO;IACrC,MAAM,QAAQ,YAAY,OAAO;IACjC,MAAM,gBAAgB,MAAM,KAAK,yBAChC,WACA,OACA,UACA,OACA,QAAQ,cACR,QAAQ,MACR,OACD;IACA,OAAO,kBAAkB,aAAa,UAAU,SAAS,aAAa;GACvE;EACD,SAAS,IAAI;GAGZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,IAAI,GAAG,UAAU,WAAW,KAAK;IAChC,OAAO,MAAM,EAAE;IACf;GACD;EACD;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;GAEZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,OAAO,MAAM,IAAI,mCAAmC,SAAS;GAC7D;EACD;CACD;CAEA,MAAM,yBACL,WACA,OACA,UACA,OACA,OACA,aACA,SAGkD;EAGlD,MAAM,UAAU,KAAK,UAAU;GAAC,QAAQ;GAAS;GAAO;EAAW,CAAC;EACpE,MAAM,WAAW,KAAK,gBAAgB,iBAAiB,SAAS,WAAW,KAAK;EAChF,IAAI,YAAY,MAAM,OAAO;EAE7B,MAAM,SAAS,MAAM,KAAK,2BAA2B,WAAW,UAAU,OAAO,OAAO,aAAa,OAAO;EAC5G,KAAK,gBAAgB,eAAe,SAAS,WAAW,MAAM;EAE9D,OAAO,KAAK,gBAAgB,iBAAiB,SAAS,WAAW,KAAK;CACvE;CAEA,MAAc,2BACb,cACA,UACA,OACA,OACA,aACA,SAGgC;EAChC,MAAM,QAAQ,gBAAgB;EAE9B,IAAI;GAYH,QAAO,MAXmB,KAAK,QAC9B,UACA,OACA,SAAS,SACT,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,WAAW,EAAE,2BAA2B,uBAAuB,YAAY,EAAE,UACxI,EACC,QAAQ,MACT,GACA,KACD,EAAA,EAEoB,SAAS,CAAC;EAC/B,SAAS,IAAI;GAIZ,IAAI,cAAc,qBAAqB,MAAM;GAE7C,OAAO,MAAM,EAAE;GACf,OAAO,CAAC;EACT;CACD;CAEA,MAQM,sBACL,UACA,OACA,OACA,MACA,SAGA,cAC0C;EAC1C,MAAM,QAAQ,gBAAgB;EAE9B,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,cAC3B,UACA,OACA,OACA,MACA,QAAQ,SACR,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;EAE9B,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,cAC3B,UACA,OACA,OACA,MACA,QAAQ,SACR,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,SAQA,cACmC;EACnC,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,OAAO,0BACZ,QAAQ,UACR,mBAAmB,QAAQ,UAAU,IAAI,GACzC,QAAQ,YACT;EACA,MAAM,MAAM,+BAA+B,YAAY,IAAI;EAC3D,MAAM,WAAW,sBAAsB,QAAQ,UAAU,KAAA,GAA4B;EACrF,MAAM,SAAS,+BAA+B,QAAQ,QAAQ,KAAK,QAAQ;EAC3E,MAAM,OAAO,QAAQ,QAAQ;EAO7B,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,iBACJ,UACA,OACA,YACA,QAAQ,SACR,MAAA,OAEA,OACA,MACD,CAAC,CAAC,OAAO,OAAgB;GAGxB,IAAI,uBAAuB,EAAE,GAC5B,MAAM,IAAI,MACT,GAAG,SAAS,KAAK,gFAClB;GAGD,MAAM;EACP,CAAC,GACF,EAAgB,aAAa,CAC9B;EACD,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,YAAY,MAAM,KAAK,QAC5B,UACA,OACA,QAAQ,SACR,GAAG,uBAAuB,UAAU,EAAE,4CACtC;IAAE,QAAQ;IAAQ,MAAM,KAAK,UAAU;KAAE,KAAK;KAAO,SAAS;KAAS,aAAa;IAAO,CAAC;GAAE,GAC9F,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,MAAM,UAAU,QAAQ,eAAe,WAAW;IAClD,IAAI,WAAW,MACd,MAAM,IAAI,MAAM,0BAA0B,GAAG,kBAAkB,YAAY,uBAAuB;IAGnG,MAAM,gBAAgB,MAAM,KAAK,yBAChC,SAAS,OAAO,wBAChB,SAAS,OAAO,iBAChB,UACA,OACA,YACA,QAAQ,MACR,OACD;IACA,OAAO,KAAK,kBAAkB,UAAU,UAAU,SAAS,aAAa,CAAC;GAC1E;EACD;EAEA,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,MAAM,OAAO,0BACZ,QAAQ,UACR,mBAAmB,QAAQ,UAAU,IAAI,GACzC,QAAQ,YACT;EACA,IAAI;GACH,MAAM,MAAM,MAAM,KAAK,iBACtB,UACA,OACA,YACA,QAAQ,SACR,MACA,iCAAiC,GACjC,OACA,YACD;GACA,OAAO,IAAI,SAAA,MAA0C,kBAAkB,IAAI;EAC5E,SAAS,IAAI;GACZ,IAAI,uBAAuB,EAAE,GAAG,OAAO;GAEvC,MAAM;EACP;CACD;;;;;;;CAQA,MAAc,iBACb,UACA,OACA,YACA,SACA,MACA,KACA,OACA,cACoB;EACpB,MAAM,SAAS,MAAM,KAAK,QACzB,UACA,OACA,SACA,GAAG,uBAAuB,UAAU,EAAE,uBAAuB,IAAI,sCACjE;GAAE,QAAQ;GAAQ,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,CAAC;EAAE,GACxD,OACA,YACD;EACA,IAAI,QAAQ,aAAa,MAAM,MAAM,IAAI,MAAM,iDAAiD;EAEhG,OAAO,OAAO,UAAU,KAAI,MAAK,EAAE,EAAE;CACtC;CAEA,cACC,UACA,OACA,OACA,MACA,SACA,OACA,cACmD;EACnD,MAAM,EAAE,aAAa,aAAa,+BAA+B,IAAI;EACrE,OAAO,KAAK,QACX,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,GAAG,uBAAuB,WAAW,EAAE,0BAA0B,uBAAuB,QAAQ,EAAE,mBACnI,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;EACA,OAAO,qBAAqB,IAAI,UAAU,OAAO,SAAS,kBAAkB;CAC7E;;;;;;;;;;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,MAAM,WAAW,MAAM,KAAK,QAC3B,UACA,OACA,SACA,GAAG,uBAAuB,KAAK,EAAE,0BAA0B,uBAAuB,YAAY,EAAE,mBAChG,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,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,MAAM;KACxB,UAAU,sBAAsB;KAChC,KAAK,OAAO,kBACX,YAAY,QAAQ,SAAS,OAAO,UACjC,KAAK,EACL,0JACA,UAAU,QAAQ,aACnB,IACC,KAAK,EAAE,2DAA2D,SAAS,IAAI,CACnF;IACD;IACA;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;AA9mCE,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;AA6DA,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;AA8GA,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;AAqDA,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;AAqHA,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;AAoDA,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;AA6DA,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;AAsIA,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;;AAoUF,SAAgB,qBAAqB,KAAgD;CACpF,OAAO,IAAI,eAAe,sBAAsB,GAAG,CAAC;AACrD"}
1
+ {"version":3,"file":"azure.js","names":["Logger"],"sources":["../../../../../../plus/integrations/src/providers/azure/azure.ts"],"sourcesContent":["import * 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 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\tAzureWorkItemState,\n\tAzureWorkItemStateCategory,\n\tWorkItem,\n} from './models.js';\nimport {\n\tazurePullRequestStatusToState,\n\tazureWorkItemsStateCategoryToState,\n\tencodeAzurePathSegment,\n\tfromAzurePullRequest,\n\tfromAzureWorkItem,\n\tgetAzurePullRequestWebUrl,\n\tisClosedAzurePullRequestStatus,\n\tisClosedAzureWorkItemStateCategory,\n\tnormalizeAzureBranchName,\n\tsanitizeAzureRepositoryUrl,\n} from './models.js';\nimport type { AzureWorkItemSearchCursor } from './search.js';\nimport {\n\tazureWorkItemBatchLimit,\n\tazureWorkItemSearchResultLimit,\n\tparseAzureWorkItemSearchCursor,\n\ttoAzureSearchPageSize,\n\ttoAzureWorkItemSearchCursorKey,\n\ttoAzureWorkItemSearchWiql,\n} from './search.js';\n\nconst forkRepositoryUrlCacheTtl = 5 * 60 * 1000;\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\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\nclass WorkItemStates {\n\tprivate readonly _categories = new Map<string, AzureWorkItemStateCategory>();\n\tprivate readonly _types = new Map<string, AzureWorkItemState[]>();\n\n\t// TODO@sergeibbb: we might need some logic for invalidating\n\tpublic getStateCategory(\n\t\tproject: string,\n\t\tworkItemType: string,\n\t\tstateName: string,\n\t): AzureWorkItemStateCategory | undefined {\n\t\treturn this._categories.get(this.getStateKey(project, workItemType, stateName));\n\t}\n\n\tpublic clear(): void {\n\t\tthis._categories.clear();\n\t\tthis._types.clear();\n\t}\n\n\tpublic saveTypeStates(project: string, workItemType: string, states: AzureWorkItemState[]): void {\n\t\tthis.clearTypeStates(project, workItemType);\n\t\tthis._types.set(this.getTypeKey(project, workItemType), states);\n\t\tfor (const state of states) {\n\t\t\tthis._categories.set(this.getStateKey(project, workItemType, state.name), state.category);\n\t\t}\n\t}\n\n\tpublic hasTypeStates(project: string, workItemType: string): boolean {\n\t\treturn this._types.has(this.getTypeKey(project, workItemType));\n\t}\n\n\tprivate clearTypeStates(project: string, workItemType: string): void {\n\t\tconst states = this._types.get(this.getTypeKey(project, workItemType));\n\t\tif (states == null) return;\n\n\t\tfor (const state of states) {\n\t\t\tthis._categories.delete(this.getStateKey(project, workItemType, state.name));\n\t\t}\n\t}\n\n\tprivate getStateKey(project: string, workItemType: string, stateName: string): string {\n\t\t// By stringifying the pair as JSON we make sure that all possible special characters are escaped\n\t\treturn JSON.stringify([project, workItemType, stateName]);\n\t}\n\n\tprivate getTypeKey(project: string, workItemType: string): string {\n\t\treturn JSON.stringify([project, workItemType]);\n\t}\n}\n\nexport class AzureDevOpsApi implements Disposable {\n\tprivate readonly _disposable: Disposable | undefined;\n\tprivate _workItemStates: WorkItemStates = new WorkItemStates();\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(private readonly config: ProviderApiConfig) {\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._workItemStates.clear();\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\ttry {\n\t\t\t\t// Try to get the Work item (wit) first with specific fields\n\t\t\t\tconst issueResult = 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\n\t\t\t\tif (issueResult != null) {\n\t\t\t\t\tconst issueType = issueResult.fields['System.WorkItemType'];\n\t\t\t\t\tconst state = issueResult.fields['System.State'];\n\t\t\t\t\tconst stateCategory = await this.getWorkItemStateCategory(\n\t\t\t\t\t\tissueType,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\ttoken,\n\t\t\t\t\t\towner,\n\t\t\t\t\t\tprojectName,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: issueResult.id.toString(),\n\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\tnodeId: issueResult.id.toString(),\n\t\t\t\t\t\tprovider: provider,\n\t\t\t\t\t\tcreatedDate: new Date(issueResult.fields['System.CreatedDate']),\n\t\t\t\t\t\tupdatedDate: new Date(issueResult.fields['System.ChangedDate']),\n\t\t\t\t\t\tstate: azureWorkItemsStateCategoryToState(stateCategory),\n\t\t\t\t\t\tclosed: isClosedAzureWorkItemStateCategory(stateCategory),\n\t\t\t\t\t\ttitle: issueResult.fields['System.Title'],\n\t\t\t\t\t\turl: issueResult._links.html.href,\n\t\t\t\t\t};\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\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\ttry {\n\t\t\t// Try to get the Work item (wit) first with specific fields\n\t\t\tconst issueResult = 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\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\tproject.resourceName,\n\t\t\t\t\tproject.name,\n\t\t\t\t\toptions,\n\t\t\t\t);\n\t\t\t\treturn fromAzureWorkItem(issueResult, provider, project, stateCategory);\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\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\t// A rejected credential is the whole point of this read failing; reporting \"no user\" instead hides it.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tscope?.error(ex, `Failed to get current user from ${baseUrl}`);\n\t\t\treturn undefined;\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): Promise<AzureWorkItemStateCategory | undefined> {\n\t\t// By installation too: this API is shared by every connection, and two installations can each have a\n\t\t// collection and project of the same name whose process maps a state to a different category.\n\t\tconst project = JSON.stringify([options.baseUrl, owner, projectName]);\n\t\tconst category = this._workItemStates.getStateCategory(project, issueType, state);\n\t\tif (category != null) return category;\n\n\t\tconst states = await this.retrieveWorkItemTypeStates(issueType, provider, token, owner, projectName, options);\n\t\tthis._workItemStates.saveTypeStates(project, issueType, states);\n\n\t\treturn this._workItemStates.getStateCategory(project, issueType, state);\n\t}\n\n\tprivate async retrieveWorkItemTypeStates(\n\t\tworkItemType: 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): Promise<AzureWorkItemState[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst issueResult = await this.request<{ value: AzureWorkItemState[]; count: number }>(\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/wit/workItemTypes/${encodeAzurePathSegment(workItemType)}/states`,\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 issueResult?.value ?? [];\n\t\t} catch (ex) {\n\t\t\t// A rejected credential must not be degraded to an empty state list: the caller caches whatever this\n\t\t\t// returns, so an empty list would pin every work item of this type to an unknown state until the\n\t\t\t// cache is dropped — long after the session recovery this error is supposed to trigger.\n\t\t\tif (ex instanceof AuthenticationError) throw ex;\n\n\t\t\tscope?.error(ex);\n\t\t\treturn [];\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\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: a WIQL query for the ordered ids, then one detail\n\t * 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 it reads a consistent set rather than re-running a query whose rows move under\n\t * an `updated` order. Each first page queries its own snapshot, so paginations of one query never share one, and\n\t * a continuation whose snapshot is gone is refused (see {@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: {\n\t\t\tbaseUrl: string;\n\t\t\tcriteria?: IssueSearchCriteria;\n\t\t\tprojectNames?: readonly string[];\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\tconst wiql = toAzureWorkItemSearchWiql(\n\t\t\toptions.criteria,\n\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\toptions.projectNames,\n\t\t);\n\t\tconst key = toAzureWorkItemSearchCursorKey(collection, wiql);\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// Keyed by credential and address, and by the query itself rather than its fingerprint: two accounts on one\n\t\t// installation must never page through each other's matches, and two queries whose fingerprints collide must\n\t\t// never share a snapshot. The full credential digest, since the token's `microHash` is truncated for logs.\n\t\t// And by the snapshot's own id: a first page queries a new snapshot rather than replacing the query's, so a\n\t\t// pagination still reading an earlier one keeps it (see `AzureWorkItemSearchCursor`).\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\twiql,\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.queryWorkItemIds(\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\toptions.baseUrl,\n\t\t\t\t\t\twiql,\n\t\t\t\t\t\tazureWorkItemSearchResultLimit + 1,\n\t\t\t\t\t\tscope,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t).catch((ex: unknown) => {\n\t\t\t\t\t\t// `$top` is expected to bound the answer; should a server refuse the match set regardless, say\n\t\t\t\t\t\t// what happened rather than surfacing a bare client error.\n\t\t\t\t\t\tif (isWorkItemLimitRefusal(ex)) {\n\t\t\t\t\t\t\tthrow new Error(\n\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);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}),\n\t\t\t\t{ cancellation: cancellation },\n\t\t\t));\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\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\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);\n\t\t\t\tvalues.push(fromAzureWorkItem(workItem, provider, project, stateCategory));\n\t\t\t}\n\t\t}\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: { baseUrl: string; criteria?: IssueSearchCriteria; projectNames?: readonly string[] },\n\t\tcancellation?: AbortSignal,\n\t): Promise<number | 'exceeds-limit'> {\n\t\tconst scope = getScopedLogger();\n\t\tconst wiql = toAzureWorkItemSearchWiql(\n\t\t\toptions.criteria,\n\t\t\teffectiveIssueSort(options.criteria?.sort),\n\t\t\toptions.projectNames,\n\t\t);\n\t\ttry {\n\t\t\tconst ids = await this.queryWorkItemIds(\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\twiql,\n\t\t\t\tazureWorkItemSearchResultLimit + 1,\n\t\t\t\tscope,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\treturn ids.length > azureWorkItemSearchResultLimit ? 'exceeds-limit' : ids.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\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): Promise<number[]> {\n\t\tconst result = await this.request<{ workItems?: { id: number }[] }>(\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: wiql }) },\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 result.workItems.map(w => w.id);\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\treturn fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls);\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\tprovider?.trackRequestException();\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(ctx: IntegrationServiceContext): AzureDevOpsApi {\n\treturn new AzureDevOpsApi(baseProviderApiConfig(ctx));\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { PullRequestUrlIdentity } from "../../../../git/utils/pullRequest.utils.js";
2
+ import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from "../../constants.js";
3
+ //#region ../plus/integrations/src/providers/azure/azure.utils.d.ts
4
+ export type AzureDevOpsIntegrationIds = GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer;
5
+ export declare function getAzurePullRequestIdentityFromMaybeUrl(search: string): (PullRequestUrlIdentity & {
6
+ provider: undefined;
7
+ }) | undefined;
8
+ export declare function getAzurePullRequestIdentityFromMaybeUrl(search: string, id: AzureDevOpsIntegrationIds): (PullRequestUrlIdentity & {
9
+ provider: AzureDevOpsIntegrationIds;
10
+ }) | undefined;
11
+ //#endregion
12
+ //# sourceMappingURL=azure.utils.d.ts.map
@@ -0,0 +1,46 @@
1
+ import { decodePathSegment } from "../../utils/domain.utils.js";
2
+ import { vstsHostnameSuffix } from "./models.js";
3
+ //#region ../plus/integrations/src/providers/azure/azure.utils.ts
4
+ /**
5
+ * Parses a pasted Azure DevOps pull request URL (`…/_git/{repo}/pullrequest/{id}`) into `{org}/{project}`:
6
+ * - `{org}/{project}/_git/…` on dev.azure.com or Server, where a leading virtual directory such as `tfs/` is dropped:
7
+ * the identity has no field for it, so the read relies on the connection's configured URL including it
8
+ * - `{org}.visualstudio.com/{project}/_git/…`, where the org is the host subdomain
9
+ * - `{org}/_git/…`, where the project is named after the repo
10
+ */
11
+ function getAzurePullRequestIdentityFromMaybeUrl(search, id) {
12
+ let url;
13
+ try {
14
+ url = new URL(search);
15
+ } catch {
16
+ return;
17
+ }
18
+ const segments = url.pathname.split("/").filter(Boolean).map(decodePathSegment);
19
+ const gitIndex = segments.indexOf("_git");
20
+ if (gitIndex < 1) return void 0;
21
+ const repo = segments[gitIndex + 1];
22
+ const prNumber = segments[gitIndex + 3];
23
+ if (repo == null || segments[gitIndex + 2] !== "pullrequest" || prNumber == null) return void 0;
24
+ const segmentBeforeGit = segments[gitIndex - 1];
25
+ let org;
26
+ let project;
27
+ if (url.hostname.toLowerCase().endsWith(".visualstudio.com")) {
28
+ org = url.hostname.slice(0, -vstsHostnameSuffix.length);
29
+ project = segmentBeforeGit;
30
+ } else if (gitIndex === 1) {
31
+ org = segmentBeforeGit;
32
+ project = repo;
33
+ } else {
34
+ org = segments[gitIndex - 2];
35
+ project = segmentBeforeGit;
36
+ }
37
+ return {
38
+ ownerAndRepo: `${org}/${project}`,
39
+ prNumber,
40
+ provider: id
41
+ };
42
+ }
43
+ //#endregion
44
+ export { getAzurePullRequestIdentityFromMaybeUrl };
45
+
46
+ //# sourceMappingURL=azure.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azure.utils.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/azure.utils.ts"],"sourcesContent":["import type { PullRequestUrlIdentity } from '@gitlens/git/utils/pullRequest.utils.js';\nimport type { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from '../../constants.js';\nimport { decodePathSegment } from '../../utils/domain.utils.js';\nimport { vstsHostnameSuffix } from './models.js';\n\nexport type AzureDevOpsIntegrationIds =\n\t| GitCloudHostIntegrationId.AzureDevOps\n\t| GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\nexport function getAzurePullRequestIdentityFromMaybeUrl(\n\tsearch: string,\n): (PullRequestUrlIdentity & { provider: undefined }) | undefined;\nexport function getAzurePullRequestIdentityFromMaybeUrl(\n\tsearch: string,\n\tid: AzureDevOpsIntegrationIds,\n): (PullRequestUrlIdentity & { provider: AzureDevOpsIntegrationIds }) | undefined;\n/**\n * Parses a pasted Azure DevOps pull request URL (`…/_git/{repo}/pullrequest/{id}`) into `{org}/{project}`:\n * - `{org}/{project}/_git/…` on dev.azure.com or Server, where a leading virtual directory such as `tfs/` is dropped:\n * the identity has no field for it, so the read relies on the connection's configured URL including it\n * - `{org}.visualstudio.com/{project}/_git/…`, where the org is the host subdomain\n * - `{org}/_git/…`, where the project is named after the repo\n */\nexport function getAzurePullRequestIdentityFromMaybeUrl(\n\tsearch: string,\n\tid?: AzureDevOpsIntegrationIds,\n): (PullRequestUrlIdentity & { provider: AzureDevOpsIntegrationIds | undefined }) | undefined {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(search);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tconst segments = url.pathname.split('/').filter(Boolean).map(decodePathSegment);\n\tconst gitIndex = segments.indexOf('_git');\n\tif (gitIndex < 1) return undefined;\n\n\tconst repo = segments[gitIndex + 1];\n\tconst prNumber = segments[gitIndex + 3];\n\tif (repo == null || segments[gitIndex + 2] !== 'pullrequest' || prNumber == null) return undefined;\n\n\tconst segmentBeforeGit = segments[gitIndex - 1];\n\n\tlet org: string;\n\tlet project: string;\n\tif (url.hostname.toLowerCase().endsWith(vstsHostnameSuffix)) {\n\t\torg = url.hostname.slice(0, -vstsHostnameSuffix.length);\n\t\tproject = segmentBeforeGit;\n\t} else if (gitIndex === 1) {\n\t\torg = segmentBeforeGit;\n\t\tproject = repo;\n\t} else {\n\t\torg = segments[gitIndex - 2];\n\t\tproject = segmentBeforeGit;\n\t}\n\n\treturn { ownerAndRepo: `${org}/${project}`, prNumber: prNumber, provider: id };\n}\n"],"mappings":";;;;;;;;;;AAuBA,SAAgB,wCACf,QACA,IAC6F;CAC7F,IAAI;CACJ,IAAI;EACH,MAAM,IAAI,IAAI,MAAM;CACrB,QAAQ;EACP;CACD;CAEA,MAAM,WAAW,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,IAAI,iBAAiB;CAC9E,MAAM,WAAW,SAAS,QAAQ,MAAM;CACxC,IAAI,WAAW,GAAG,OAAO,KAAA;CAEzB,MAAM,OAAO,SAAS,WAAW;CACjC,MAAM,WAAW,SAAS,WAAW;CACrC,IAAI,QAAQ,QAAQ,SAAS,WAAW,OAAO,iBAAiB,YAAY,MAAM,OAAO,KAAA;CAEzF,MAAM,mBAAmB,SAAS,WAAW;CAE7C,IAAI;CACJ,IAAI;CACJ,IAAI,IAAI,SAAS,YAAY,CAAC,CAAC,SAAA,mBAA2B,GAAG;EAC5D,MAAM,IAAI,SAAS,MAAM,GAAG,CAAC,mBAAmB,MAAM;EACtD,UAAU;CACX,OAAO,IAAI,aAAa,GAAG;EAC1B,MAAM;EACN,UAAU;CACX,OAAO;EACN,MAAM,SAAS,WAAW;EAC1B,UAAU;CACX;CAEA,OAAO;EAAE,cAAc,GAAG,IAAI,GAAG;EAAqB;EAAU,UAAU;CAAG;AAC9E"}
@@ -5,6 +5,7 @@ import { Issue } from "../../../../git/models/issue.js";
5
5
  import { PullRequest, PullRequestMember, PullRequestMergeableState, PullRequestReviewDecision, PullRequestReviewState, PullRequestReviewer } from "../../../../git/models/pullRequest.js";
6
6
  import { ProviderIssue } from "../models.js";
7
7
  //#region ../plus/integrations/src/providers/azure/models.d.ts
8
+ export declare const vstsHostnameSuffix = ".visualstudio.com";
8
9
  export interface AzureRepositoryDescriptor extends ResourceDescriptor {
9
10
  owner: string;
10
11
  name: string;
@@ -350,6 +350,6 @@ function toAzureWorkItemIteration(path) {
350
350
  } : void 0;
351
351
  }
352
352
  //#endregion
353
- export { azurePullRequestStatusToState, azureWorkItemsStateCategoryToState, encodeAzurePathSegment, fromAzurePullRequest, fromAzurePullRequestMergeStatusToMergeableState, fromAzurePullRequestVoteToReviewState, fromAzureReviewerToPullRequestMember, fromAzureUserWithVoteToReviewer, fromAzureWorkItem, fromAzureWorkItemToProviderIssue, getAzureDevOpsOwner, getAzurePullRequestReviewDecision, getAzurePullRequestWebUrl, isClosedAzurePullRequestStatus, isClosedAzureWorkItemStateCategory, isVsts, normalizeAzureBranchName, sanitizeAzureRepositoryUrl };
353
+ export { azurePullRequestStatusToState, azureWorkItemsStateCategoryToState, encodeAzurePathSegment, fromAzurePullRequest, fromAzurePullRequestMergeStatusToMergeableState, fromAzurePullRequestVoteToReviewState, fromAzureReviewerToPullRequestMember, fromAzureUserWithVoteToReviewer, fromAzureWorkItem, fromAzureWorkItemToProviderIssue, getAzureDevOpsOwner, getAzurePullRequestReviewDecision, getAzurePullRequestWebUrl, isClosedAzurePullRequestStatus, isClosedAzureWorkItemStateCategory, isVsts, normalizeAzureBranchName, sanitizeAzureRepositoryUrl, vstsHostnameSuffix };
354
354
 
355
355
  //# sourceMappingURL=models.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/models.ts"],"sourcesContent":["import type { IssueIteration, IssueMember } from '@gitlens/git/models/issue.js';\nimport { Issue, RepositoryAccessLevel } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequestState } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequestMember, PullRequestReviewer } from '@gitlens/git/models/pullRequest.js';\nimport {\n\tPullRequest,\n\tPullRequestMergeableState,\n\tPullRequestReviewDecision,\n\tPullRequestReviewState,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport type { ProviderAccount, ProviderIssue } from '../models.js';\n\nconst vstsHostnameSuffix = '.visualstudio.com';\n\nexport interface AzureRepositoryDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n\tproject?: string;\n\tvirtualDirectory?: string;\n}\n\nexport interface AzureOrganizationDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n}\n\nexport interface AzureProjectDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n\tresourceId: string;\n\tresourceName: string;\n}\n\nexport interface AzureRemoteRepositoryDescriptor extends ResourceDescriptor {\n\tid: string;\n\tnodeId?: string;\n\tresourceName: string;\n\tname: string;\n\tprojectName?: string;\n\turl?: string;\n\tcloneUrlHttps?: string;\n\tcloneUrlSsh?: string;\n}\n\nexport interface AzureProjectInputDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n}\n\nexport type AzureWorkItemStateCategory = 'Proposed' | 'InProgress' | 'Resolved' | 'Completed' | 'Removed';\n\nexport function isClosedAzureWorkItemStateCategory(category: AzureWorkItemStateCategory | undefined): boolean {\n\treturn category === 'Completed' || category === 'Resolved' || category === 'Removed';\n}\n\nexport function azureWorkItemsStateCategoryToState(\n\tcategory: AzureWorkItemStateCategory | undefined,\n): IssueOrPullRequestState {\n\tswitch (category) {\n\t\tcase 'Resolved':\n\t\tcase 'Completed':\n\t\tcase 'Removed':\n\t\t\treturn 'closed';\n\t\tcase 'Proposed':\n\t\tcase 'InProgress':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\n\nexport interface AzureLink {\n\thref: string;\n}\n\nexport interface AzureUser {\n\tdisplayName: string;\n\turl: string;\n\t_links: {\n\t\tavatar: AzureLink;\n\t};\n\tid: string;\n\tuniqueName: string;\n\timageUrl: string;\n\tdescriptor?: string;\n}\n\nexport interface AzureUserWithVote extends AzureUser {\n\tisFlagged?: boolean;\n\thasDeclined?: boolean;\n\tisReapprove?: boolean;\n\tisRequired?: boolean;\n\tvote?: AzurePullRequestVote;\n}\n\nexport type AzurePullRequestVote =\n\t| 10 // approved\n\t| 5 // approved with suggestions\n\t| 0 // no vote\n\t| -5 // waiting for author\n\t| -10; // rejected\n\nexport interface AzureWorkItemCommentVersionRef {\n\tcommentId: number;\n\tcreatedInRevision: number;\n\tisDeleted: boolean;\n\ttext: string;\n\turl: string;\n\tversion: number;\n}\n\nexport interface AzureWorkItemRelation {\n\tattributes: {\n\t\t[key: string]: string;\n\t};\n\trelation: string;\n\turl: string;\n}\n\nexport interface WorkItem {\n\t_links: {\n\t\tfields: AzureLink;\n\t\thtml: AzureLink;\n\t\tself: AzureLink;\n\t\tworkItemComments: AzureLink;\n\t\tworkItemRevisions: AzureLink;\n\t\tworkItemType: AzureLink;\n\t\tworkItemUpdates: AzureLink;\n\t};\n\tfields: {\n\t\t//'System.AreaPath': string;\n\t\t'System.TeamProject': string;\n\t\t'System.IterationPath'?: string;\n\t\t'System.WorkItemType': string;\n\t\t'System.State': string;\n\t\t// 'System.Reason': string;\n\t\t'System.AssignedTo': AzureUser;\n\t\t'System.CreatedDate': string;\n\t\t'System.CreatedBy': AzureUser;\n\t\t'System.ChangedDate': string;\n\t\t'System.ChangedBy': AzureUser;\n\t\t'System.CommentCount': number;\n\t\t'System.Description': string;\n\t\t'System.Title': string;\n\t\t'Microsoft.VSTS.Common.ClosedDate': string;\n\t\t// 'Microsoft.VSTS.Common.StateChangeDate': string;\n\t\t// 'Microsoft.VSTS.Common.Priority': number;\n\t\t// 'Microsoft.VSTS.Common.Severity': string;\n\t\t// 'Microsoft.VSTS.Common.ValueArea': string;\n\t};\n\tid: number;\n\trev: number;\n\turl: string;\n\tcommentVersionRef?: AzureWorkItemCommentVersionRef;\n\trelations?: AzureWorkItemRelation[];\n}\n\nexport interface AzureWorkItemState {\n\tname: string;\n\tcolor: string;\n\tcategory: AzureWorkItemStateCategory;\n}\n\nexport type AzurePullRequestStatus = 'abandoned' | 'active' | 'completed' | 'notSet';\nexport function azurePullRequestStatusToState(status: AzurePullRequestStatus): IssueOrPullRequestState {\n\tswitch (status) {\n\t\tcase 'abandoned':\n\t\t\treturn 'closed';\n\t\tcase 'completed':\n\t\t\treturn 'merged';\n\t\tcase 'active':\n\t\tcase 'notSet':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\nexport function isClosedAzurePullRequestStatus(status: AzurePullRequestStatus): boolean {\n\treturn azurePullRequestStatusToState(status) !== 'opened';\n}\n\nexport type AzureProjectState = 'createPending' | 'deleted' | 'deleting' | 'new' | 'unchanged' | 'wellFormed';\nexport type AzureProjectVisibility = 'private' | 'public';\n\nexport interface AzureProject {\n\tid: string;\n\tname: string;\n\turl: string;\n\tstate: AzureProjectState;\n\trevision: number;\n\tvisibility: AzureProjectVisibility;\n\tlastUpdateTime: string;\n}\n\nexport interface AzureRepositoryReference {\n\tid: string;\n\tname: string;\n\turl: string;\n\tremoteUrl?: string;\n}\n\nexport interface AzureRepository extends AzureRepositoryReference {\n\tproject: AzureProject;\n\tsize: number;\n\tremoteUrl: string;\n\tsshUrl: string;\n\twebUrl: string;\n\tisDisabled: boolean;\n\tisInMaintenance: boolean;\n}\n\nexport interface AzurePullRequestRepository extends AzureRepositoryReference {\n\tproject: Pick<AzureProject, 'id' | 'name'>;\n}\n\n/** The URL fields of a repository response — all the fork lookup reads, and all an older `api-version` promises. */\nexport type AzureRepositoryUrls = Partial<Pick<AzureRepository, 'webUrl' | 'remoteUrl'>>;\n\n/** The `GET .../_apis/git/repositories/{repositoryId or name}` response, adding fork/default-branch fields to {@link AzureRepository}. */\nexport interface AzureRepositoryWithMetadata extends AzureRepository {\n\t/** Fully-qualified ref, e.g. `refs/heads/main`. */\n\tdefaultBranch?: string;\n\tisFork?: boolean;\n\tparentRepository?: {\n\t\tid: string;\n\t\tname?: string;\n\t\tproject?: { id: string; name: string };\n\t};\n}\n\nexport interface AzureGitUser {\n\tdate?: string;\n\temail?: string;\n\timageUrl?: string;\n\tname: string;\n}\n\nexport interface AzureGitCommitRef {\n\tcommitId: string;\n\turl: string;\n}\n\nexport interface AzureGitCommit {\n\t_links: {\n\t\tchanges: AzureLink;\n\t\trepository: AzureLink;\n\t\tself: AzureLink;\n\t\tweb: AzureLink;\n\t};\n\tauthor: AzureGitUser;\n\tcomment: string;\n\tcommentTruncated?: boolean;\n\tcommitId: string;\n\tcommitTooManyChanges?: boolean;\n\tcommitter: AzureGitUser;\n\tparents: string[];\n\tpush: {\n\t\tdate: string;\n\t\tpushedBy: AzureUser;\n\t\tpushId: number;\n\t};\n\tremoteUrl: string;\n\tstatuses?: AzureGitStatus[];\n\ttreeId: string;\n\turl: string;\n\tworkItems?: AzureResourceRef[];\n}\n\nexport interface AzureResourceRef {\n\tid: string;\n\turl: string;\n}\n\nexport interface AzurePullRequestCompletionOptions {\n\tautoCompleteIgnoreConflicts: number[];\n\tbypassPolicy: boolean;\n\tbypassReason: string;\n\tdeleteSourceBranch: boolean;\n\tmergeCommitMessage: string;\n\tmergeStrategy: 'noFastForward' | 'rebase' | 'rebaseMerge' | 'squash';\n\tsquashMerge: boolean;\n\ttransitionWorkItems: boolean;\n\ttriggeredByAutoComplete: boolean;\n}\n\nexport interface AzureGitStatus {\n\tcontext: {\n\t\tname: string;\n\t\tgenre: string;\n\t};\n\tcreatedBy: AzureUser;\n\tcreateDate: string;\n\tdescription: string;\n\tstate: 'error' | 'failed' | 'notApplicable' | 'notSet' | 'pending' | 'succeeded';\n\ttargetUrl: string;\n\tupdateDate: string;\n}\n\nexport interface AzureGitForkRef {\n\tcreator: AzureUser;\n\tisLocked: boolean;\n\tisLockedBy: AzureUser;\n\tname: string;\n\tobjectId: string;\n\tpeeledObjectId: string;\n\trepository: AzureRepositoryReference;\n\tstatuses: AzureGitStatus[];\n\turl: string;\n}\n\nexport interface AzureWebApiTagDefinition {\n\tactive: boolean;\n\tid: string;\n\tname: string;\n\turl: string;\n}\n\nexport interface AzureGitPullRequestMergeOptions {\n\tconflictAuthorshipCommits: boolean;\n\tdetectRenameFalsePositives: boolean;\n\tdisableRenames: boolean;\n}\n\nexport type AzurePullRequestAsyncStatus =\n\t| 'conflicts'\n\t| 'failure'\n\t| 'notSet'\n\t| 'queued'\n\t| 'rejectedByPolicy'\n\t| 'succeeded';\n\nexport interface AzurePullRequest {\n\trepository: AzurePullRequestRepository;\n\tpullRequestId: number;\n\tcodeReviewId: number;\n\tstatus: AzurePullRequestStatus;\n\tcreatedBy: AzureUser;\n\tcreationDate: string;\n\tclosedDate?: string;\n\tclosedBy?: AzureUser; // Can be missed even if closedDate is presented.\n\ttitle: string;\n\tdescription: string;\n\tforkSource?: AzureGitForkRef;\n\tsourceRefName: string;\n\ttargetRefName: string;\n\tisDraft: boolean;\n\tmergeId: string;\n\tmergeStatus?: AzurePullRequestAsyncStatus;\n\tlastMergeCommit?: AzureGitCommitRef;\n\tlastMergeSourceCommit: AzureGitCommitRef;\n\tlastMergeTargetCommit: AzureGitCommitRef;\n\treviewers: AzureUserWithVote[];\n\turl: string;\n\tsupportsIterations: boolean;\n}\n\nexport interface AzurePullRequestWithLinks extends AzurePullRequest {\n\t_links: {\n\t\tself: AzureLink;\n\t\trepository: AzureLink;\n\t\tworkItems: AzureLink;\n\t\tsourceBranch: AzureLink;\n\t\ttargetBranch: AzureLink;\n\t\tstatuses: AzureLink;\n\t\tsourceCommit: AzureLink;\n\t\ttargetCommit: AzureLink;\n\t\tcreatedBy: AzureLink;\n\t\titerations: AzureLink;\n\t};\n\tartifactId: string;\n\tautoCompleteSetBy?: AzureUser;\n\tcommits?: AzureGitCommitRef[];\n\tcompletionOptions?: AzurePullRequestCompletionOptions;\n\tcompletionQueueTime?: string;\n\thasMultipleMergeBases?: boolean;\n\tlabels?: AzureWebApiTagDefinition[];\n\tmergeFailureMessage?: string;\n\tmergeFailureType?: 'caseSensitive' | 'none' | 'objectTooLarge' | 'unknown';\n\tmergeOptions?: AzureGitPullRequestMergeOptions;\n\tremoteUrl?: string;\n\tworkItemRefs?: AzureResourceRef[];\n}\n\nexport function getAzureDevOpsOwner(url: URL): string {\n\treturn url.pathname.split('/')[1];\n}\nexport function isVsts(domain: string): boolean {\n\treturn domain.endsWith(vstsHostnameSuffix);\n}\n\n/**\n * Encodes one route segment of an Azure DevOps request (#5878): `encodeURIComponent` leaves a dot segment as is, and\n * the URL would resolve it away, re-scoping the request instead of failing it.\n */\nexport function encodeAzurePathSegment(value: string): string {\n\tif (value === '.' || value === '..') throw new Error(`Invalid Azure path segment '${value}'.`);\n\n\treturn encodeURIComponent(value);\n}\n\n/**\n * `baseUrl` and `owner` are the authoritative prefix used for the API request. The payload URL cannot supply that\n * prefix safely because it may address the repository by id or name an untrusted host.\n */\nfunction getAzureRepositoryWebUrl(baseUrl: string, owner: string, projectName: string, repoName: string): string {\n\tconst repoPath = `${encodeURIComponent(owner)}/${encodeURIComponent(projectName)}/_git/${encodeURIComponent(repoName)}`;\n\treturn `${baseUrl.replace(/\\/+$/, '')}/${repoPath}`;\n}\n\nexport function getAzurePullRequestWebUrl(pr: AzurePullRequest, baseUrl: string, owner: string): string {\n\tconst repoUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\treturn `${repoUrl}/pullrequest/${pr.pullRequestId}`;\n}\n\n/**\n * Whether `url` and `expected` are the two cloud spellings of one organization: a cloud organization answers on both\n * `dev.azure.com/{owner}` and the legacy `{owner}.visualstudio.com`, so those name the same host. Anything else is\n * somewhere we didn't ask.\n */\nfunction isSameAzureCloudOrganization(url: URL, expected: URL, expectedOwner: string): boolean {\n\tif (url.protocol !== expected.protocol) return false;\n\n\tconst owner = expectedOwner.toLowerCase();\n\tif (expected.hostname !== 'dev.azure.com' && !isVsts(expected.hostname)) return false;\n\n\tif (url.hostname === 'dev.azure.com') return getAzureDevOpsOwner(url).toLowerCase() === owner;\n\n\treturn url.hostname === `${owner}${vstsHostnameSuffix}`;\n}\n\n/**\n * Whether `url` sits under the collection the integration is configured to talk to — `{expectedUrl}/{expectedOwner}`.\n * A matching origin says nothing on its own: Azure organizations and self-hosted collections are path segments.\n */\nfunction isUnderAzureCollection(url: URL, expected: URL, expectedOwner: string): boolean {\n\tconst base = expected.pathname.replace(/\\/+$/, '');\n\tconst prefix = `${base}/${encodeURIComponent(expectedOwner)}/`.toLowerCase();\n\treturn `${url.pathname}/`.toLowerCase().startsWith(prefix);\n}\n\n/**\n * Restricts a provider-supplied repository URL to the integration's collection and removes credentials, query, and\n * fragment before a consumer can use it as a git remote. Takes the value as the payloads carry it — every one of\n * these URLs is optional — so a caller never has to spell the absent case itself.\n */\nexport function sanitizeAzureRepositoryUrl(\n\tvalue: string | undefined,\n\texpectedUrl: string,\n\texpectedOwner: string,\n): string | undefined {\n\tif (value == null) return undefined;\n\n\tlet expected: URL;\n\tlet url: URL;\n\ttry {\n\t\texpected = new URL(expectedUrl);\n\t\turl = new URL(value);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (expected.protocol !== 'https:' && expected.protocol !== 'http:') return undefined;\n\tif (!expectedOwner) return undefined;\n\n\tif (url.origin === expected.origin) {\n\t\tif (!isUnderAzureCollection(url, expected, expectedOwner)) return undefined;\n\t} else if (!isSameAzureCloudOrganization(url, expected, expectedOwner)) {\n\t\treturn undefined;\n\t}\n\n\turl.username = '';\n\turl.password = '';\n\turl.search = '';\n\turl.hash = '';\n\treturn url.toString();\n}\n\n/**\n * The clone URL the payload carries for a repository, restricted to the integration's collection and then\n * cross-checked against the repository that payload names.\n *\n * The collection check alone is not enough for a URL that is READ rather than built. Every other repository URL the\n * model reports is built from the authoritative prefix, so the payload cannot move it; this one cannot be built,\n * because Azure spells a repository for git the same way it spells it for the web only by convention. A URL under\n * the right collection may still name a DIFFERENT repository in it, which would leave `cloneHttps` describing one\n * repository while `url` describes another.\n *\n * Azure ends a clone URL with `_git/{repo}` on every host style — including the short `{owner}/_git/{repo}` form it\n * uses when a repository carries its project's name, and the legacy `{owner}.visualstudio.com` spelling that has no\n * organization segment at all — so the repository name is the one part comparable across all of them. A same-named\n * repository in another project of the same organization is the single substitution this cannot catch.\n */\nfunction getAzureRepositoryCloneUrl(\n\trepository: AzureRepositoryReference,\n\tbaseUrl: string,\n\towner: string,\n): string | undefined {\n\tconst sanitized = sanitizeAzureRepositoryUrl(repository.remoteUrl, baseUrl, owner);\n\tif (sanitized == null) return undefined;\n\n\t// Safe to parse: `sanitizeAzureRepositoryUrl` returns what it already parsed.\n\tconst segments = new URL(sanitized).pathname.split('/').filter(s => s.length > 0);\n\tconst name = segments.pop();\n\tif (name == null || segments.pop() !== '_git') return undefined;\n\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(name);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\t// Azure resolves project and repository names case-insensitively, so comparing them any other way would refuse a\n\t// URL the provider considers the same one.\n\treturn decoded.toLowerCase() === repository.name.toLowerCase() ? sanitized : undefined;\n}\n\nexport function fromAzurePullRequestMergeStatusToMergeableState(\n\tmergeStatus: AzurePullRequestAsyncStatus,\n): PullRequestMergeableState {\n\tswitch (mergeStatus) {\n\t\tcase 'conflicts':\n\t\t\treturn PullRequestMergeableState.Conflicting;\n\t\tcase 'failure':\n\t\t\treturn PullRequestMergeableState.FailingChecks;\n\t\tcase 'rejectedByPolicy':\n\t\t\treturn PullRequestMergeableState.BlockedByPolicy;\n\t\tcase 'succeeded':\n\t\t\treturn PullRequestMergeableState.Mergeable;\n\t\tcase 'notSet':\n\t\tcase 'queued':\n\t\tdefault:\n\t\t\treturn PullRequestMergeableState.Unknown;\n\t}\n}\n\nexport function fromAzurePullRequestVoteToReviewState(vote: AzurePullRequestVote): PullRequestReviewState {\n\tswitch (vote) {\n\t\tcase 10:\n\t\tcase 5:\n\t\t\treturn PullRequestReviewState.Approved;\n\t\tcase 0:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t\tcase -5:\n\t\tcase -10:\n\t\t\treturn PullRequestReviewState.ChangesRequested;\n\t\tdefault:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t}\n}\n\nexport function fromAzureUserWithVoteToReviewer(reviewer: AzureUserWithVote): PullRequestReviewer {\n\treturn {\n\t\tisCodeOwner: undefined,\n\t\treviewer: {\n\t\t\tavatarUrl: reviewer.imageUrl,\n\t\t\tid: reviewer.id,\n\t\t\tname: reviewer.displayName,\n\t\t\tusername: reviewer.uniqueName,\n\t\t\turl: reviewer.url,\n\t\t},\n\t\tstate: fromAzurePullRequestVoteToReviewState(reviewer.vote ?? 0),\n\t};\n}\n\nexport function getAzurePullRequestReviewDecision(\n\tvotes: AzurePullRequestVote[],\n): PullRequestReviewDecision | undefined {\n\tconst reviewStates = votes.map(vote => fromAzurePullRequestVoteToReviewState(vote));\n\tif (reviewStates.includes(PullRequestReviewState.ChangesRequested)) {\n\t\treturn PullRequestReviewDecision.ChangesRequested;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.ReviewRequested)) {\n\t\treturn PullRequestReviewDecision.ReviewRequired;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.Approved)) {\n\t\treturn PullRequestReviewDecision.Approved;\n\t}\n\n\treturn undefined;\n}\n\nexport function fromAzureReviewerToPullRequestMember(reviewer: AzureUser): PullRequestMember {\n\treturn {\n\t\tavatarUrl: reviewer.imageUrl,\n\t\tid: reviewer.id,\n\t\tname: reviewer.displayName,\n\t\tusername: reviewer.uniqueName,\n\t\turl: reviewer.url,\n\t};\n}\n\nexport function normalizeAzureBranchName(branchName: string): string {\n\treturn branchName.startsWith('refs/heads/') ? branchName.replace('refs/heads/', '') : branchName;\n}\n\nfunction fromAzureUserToMember(user: AzureUser, type: 'issue'): IssueMember;\nfunction fromAzureUserToMember(user: AzureUser, type: 'pullRequest'): PullRequestMember;\nfunction fromAzureUserToMember(user: AzureUser, _type: 'issue' | 'pullRequest'): PullRequestMember | IssueMember {\n\treturn {\n\t\tavatarUrl: user.imageUrl,\n\t\tid: user.id,\n\t\tname: user.displayName,\n\t\tusername: user.uniqueName,\n\t\turl: user.url,\n\t};\n}\n\n/**\n * The URLs of a cross-repository pull request's fork, resolved by a lookup because the pull request payload embeds\n * only an abbreviated reference to it: `url` is the URL the head ref reports — the fork's web URL, or its clone URL\n * when the response carries no web URL — and `cloneHttps` its HTTPS clone URL.\n *\n * A fork that resolves neither is reported as no fork at all rather than as an object with nothing in it, so `url`\n * is always present here; `cloneHttps` alone is best-effort, and is absent when the response omits it or its URL\n * sits outside the integration's collection.\n */\nexport interface AzureForkRepositoryUrls {\n\turl: string;\n\tcloneHttps: string | undefined;\n}\n\n/**\n * `baseUrl` and `owner` are the prefix the pull request was read through; every URL the model reports is built from\n * them. The required `forkRepositoryUrls` argument makes each call site resolve the best-effort fork URLs explicitly.\n */\nexport function fromAzurePullRequest(\n\tpr: AzurePullRequest,\n\tprovider: Provider,\n\towner: string,\n\tbaseUrl: string,\n\tforkRepositoryUrls: AzureForkRepositoryUrls | undefined,\n): PullRequest {\n\tconst baseRepositoryUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\tconst baseCloneHttps = getAzureRepositoryCloneUrl(pr.repository, baseUrl, owner);\n\n\tconst forkRepository = pr.forkSource?.repository;\n\tconst headRepositoryUrl = forkRepository == null ? baseRepositoryUrl : forkRepositoryUrls?.url;\n\tconst headCloneHttps = forkRepository == null ? baseCloneHttps : forkRepositoryUrls?.cloneHttps;\n\n\treturn new PullRequest(\n\t\tprovider,\n\t\tfromAzureUserToMember(pr.createdBy, 'pullRequest'),\n\t\tpr.pullRequestId.toString(),\n\t\tpr.pullRequestId.toString(),\n\t\tpr.title,\n\t\tgetAzurePullRequestWebUrl(pr, baseUrl, owner),\n\t\t{\n\t\t\towner: owner,\n\t\t\trepo: pr.repository.name,\n\t\t\tid: pr.repository.id,\n\t\t\t// TODO: Remove this assumption once actual access level is available\n\t\t\taccessLevel: RepositoryAccessLevel.Write,\n\t\t},\n\t\tazurePullRequestStatusToState(pr.status),\n\t\tnew Date(pr.creationDate),\n\t\tnew Date(pr.closedDate || pr.creationDate),\n\t\tpr.closedDate ? new Date(pr.closedDate) : undefined,\n\t\tpr.closedDate && pr.status === 'completed' ? new Date(pr.closedDate) : undefined,\n\t\tfromAzurePullRequestMergeStatusToMergeableState(pr.mergeStatus ?? 'notSet'),\n\t\tundefined,\n\t\t{\n\t\t\tbase: {\n\t\t\t\tbranch: pr.targetRefName ? normalizeAzureBranchName(pr.targetRefName) : '',\n\t\t\t\tsha: pr.lastMergeTargetCommit?.commitId ?? '',\n\t\t\t\trepo: pr.repository.name,\n\t\t\t\towner: owner,\n\t\t\t\texists: pr.targetRefName != null,\n\t\t\t\turl: baseRepositoryUrl,\n\t\t\t\tcloneHttps: baseCloneHttps,\n\t\t\t},\n\t\t\thead: {\n\t\t\t\tbranch: pr.sourceRefName ? normalizeAzureBranchName(pr.sourceRefName) : '',\n\t\t\t\tsha: pr.lastMergeSourceCommit?.commitId ?? '',\n\t\t\t\trepo: forkRepository?.name ?? pr.repository.name,\n\t\t\t\towner: owner,\n\t\t\t\texists: pr.sourceRefName != null,\n\t\t\t\turl: headRepositoryUrl,\n\t\t\t\tcloneHttps: headCloneHttps,\n\t\t\t},\n\t\t\tisCrossRepository: pr.forkSource != null,\n\t\t},\n\t\tpr.isDraft,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tgetAzurePullRequestReviewDecision(pr.reviewers?.filter(r => r.isRequired).map(r => r.vote ?? 0) ?? []),\n\t\tpr.reviewers.filter(r => r.vote == null || r.vote === 0).map(r => fromAzureUserWithVoteToReviewer(r)),\n\t\tpr.reviewers.filter(r => r.vote != null && r.vote !== 0).map(r => fromAzureUserWithVoteToReviewer(r)),\n\t\tpr.reviewers.map(r => fromAzureReviewerToPullRequestMember(r)),\n\t\tundefined,\n\t\t{\n\t\t\tid: pr.repository?.project?.id,\n\t\t\tname: pr.repository.project.name,\n\t\t\tresourceId: '', // TODO: This is a workaround until we can get the org id here.\n\t\t\tresourceName: owner,\n\t\t},\n\t);\n}\n\nexport function fromAzureWorkItem(\n\tworkItem: WorkItem,\n\tprovider: Provider,\n\tproject: AzureProjectDescriptor,\n\tstateCategory?: AzureWorkItemStateCategory,\n): Issue {\n\treturn new Issue(\n\t\tprovider,\n\t\tworkItem.id.toString(),\n\t\tworkItem.id.toString(),\n\t\tworkItem.fields['System.Title'],\n\t\tworkItem._links.html.href,\n\t\tnew Date(workItem.fields['System.CreatedDate']),\n\t\tnew Date(workItem.fields['System.ChangedDate']),\n\t\tisClosedAzureWorkItemStateCategory(stateCategory),\n\t\tazureWorkItemsStateCategoryToState(stateCategory),\n\t\tfromAzureUserToMember(workItem.fields['System.CreatedBy'], 'issue'),\n\t\tworkItem.fields['System.AssignedTo'] != null\n\t\t\t? [fromAzureUserToMember(workItem.fields['System.AssignedTo'], 'issue')]\n\t\t\t: [],\n\t\tundefined,\n\t\tworkItem.fields['Microsoft.VSTS.Common.ClosedDate']\n\t\t\t? new Date(workItem.fields['Microsoft.VSTS.Common.ClosedDate'])\n\t\t\t: undefined,\n\t\tundefined,\n\t\tworkItem.fields['System.CommentCount'],\n\t\tundefined,\n\t\tworkItem.fields['System.Description'],\n\t\tproject,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\ttoWorkItemIterations(workItem.fields['System.IterationPath']),\n\t);\n}\n\n/**\n * Azure reports the iteration as a backslash-delimited path rooted at the project. The project root is its default\n * for a work item with no sprint, so only a nested path names one; the path is the identity because Azure supplies\n * no iteration id here.\n *\n * Mirrors `normalizeIteration` in provider-apis so the same work item yields the same identity whether it is read\n * here or through the SDK. That contract keeps the path **verbatim** — it is what the iteration is matched back by,\n * so it is deliberately not trimmed — and trims only the display name.\n */\nfunction toWorkItemIterations(path: string | undefined): IssueIteration[] | undefined {\n\tif (!path) return undefined;\n\n\tconst segments = path.split('\\\\');\n\tif (segments.length < 2) return undefined;\n\n\tconst name = segments.at(-1)?.trim();\n\treturn name ? [{ id: path, name: name }] : undefined;\n}\n\n/**\n * A work item as `GET …/_apis/wit/workitems/{id}?$expand=Links` returns it. Loosely typed because\n * {@link fromAzureWorkItemToProviderIssue} checks every field it reads, as provider-apis does.\n */\nexport interface AzureWorkItemResponse {\n\tid?: unknown;\n\tfields?: Record<string, unknown>;\n\t_links?: { html?: { href?: unknown } };\n}\n\n/**\n * Converts a work item exactly as provider-apis' `getIssuesForAzureProject` converts each row it reads, so a work\n * item read by id reaches `toIssueShape` identical to the list read's row for it. provider-apis exports neither\n * that converter nor a single work item read, so this mirrors it field for field; the batch issue tests compare\n * the two, so a provider-apis change that this misses fails there.\n *\n * `undefined` for a work item the SDK would skip: no positive integer id, no title, or no valid created date.\n * `state.name` is the raw `System.State`, as on every list read here, which passes the SDK no state-name map.\n */\nexport function fromAzureWorkItemToProviderIssue(\n\tworkItem: AzureWorkItemResponse,\n\tnamespace: string,\n\tproject: string,\n): ProviderIssue | undefined {\n\tconst fields = workItem.fields;\n\tconst id = workItem.id;\n\tconst title = fields?.['System.Title'];\n\tconst createdDate = parseAzureWorkItemDate(fields?.['System.CreatedDate']);\n\tif (\n\t\tfields == null ||\n\t\ttypeof id !== 'number' ||\n\t\t!Number.isSafeInteger(id) ||\n\t\tid <= 0 ||\n\t\ttypeof title !== 'string' ||\n\t\t!title.trim() ||\n\t\tcreatedDate == null\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst assignee = fromAzureWorkItemIdentity(fields['System.AssignedTo']);\n\tconst commentCount = fields['System.CommentCount'];\n\tconst url = workItem._links?.html?.href;\n\tconst tags = fields['System.Tags'];\n\tconst state = fields['System.State'];\n\tconst type = fields['System.WorkItemType'];\n\tconst description = fields['System.Description'];\n\treturn {\n\t\tid: id.toString(),\n\t\tnumber: id.toString(),\n\t\ttitle: title,\n\t\tcommentCount: typeof commentCount === 'number' && Number.isFinite(commentCount) ? commentCount : null,\n\t\tauthor: fromAzureWorkItemIdentity(fields['System.CreatedBy']),\n\t\tclosedDate: parseAzureWorkItemDate(fields['Microsoft.VSTS.Common.ClosedDate']),\n\t\tcreatedDate: createdDate,\n\t\tupdatedDate: parseAzureWorkItemDate(fields['System.ChangedDate']),\n\t\turl: typeof url === 'string' ? url : null,\n\t\tassignees: assignee != null ? [assignee] : [],\n\t\tdescription: typeof description === 'string' ? description : null,\n\t\tstate: typeof state === 'string' && state ? { name: state, color: null } : null,\n\t\ttype: typeof type === 'string' ? type : null,\n\t\titeration: toAzureWorkItemIteration(fields['System.IterationPath']),\n\t\trepository: null,\n\t\tproject: { namespace: namespace, name: project, resourceId: null, key: null, id: null },\n\t\tupvoteCount: 0,\n\t\tlabels: (typeof tags === 'string' ? tags.split(';') : []).map(tag => ({\n\t\t\tcolor: null,\n\t\t\tdescription: null,\n\t\t\tid: null,\n\t\t\tname: tag.trim(),\n\t\t})),\n\t};\n}\n\n/** An identity field, as provider-apis maps it: `name` from `uniqueName`, `username` from `displayName`. */\nfunction fromAzureWorkItemIdentity(value: unknown): ProviderAccount | null {\n\tconst identity = value as\n\t\t| { id?: unknown; uniqueName?: unknown; displayName?: unknown; _links?: { avatar?: { href?: unknown } } }\n\t\t| null\n\t\t| undefined;\n\tif (typeof identity?.id !== 'string' || !identity.id.trim()) return null;\n\n\tconst avatarUrl = identity._links?.avatar?.href;\n\treturn {\n\t\tavatarUrl: typeof avatarUrl === 'string' ? avatarUrl : null,\n\t\temail: null,\n\t\tid: identity.id,\n\t\tname: typeof identity.uniqueName === 'string' ? identity.uniqueName : null,\n\t\tusername: typeof identity.displayName === 'string' ? identity.displayName : null,\n\t\turl: null,\n\t};\n}\n\n/** Only a UTC ISO timestamp naming a real calendar date and time, as provider-apis accepts; anything else is `null`. */\nfunction parseAzureWorkItemDate(value: unknown): Date | null {\n\tif (typeof value !== 'string') return null;\n\n\tconst match = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,7})?Z$/.exec(value);\n\tif (match == null) return null;\n\n\tconst [year, month, day, hour, minute, second] = match.slice(1).map(Number);\n\tconst isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n\tconst daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\tif (month < 1 || month > 12 || day < 1 || day > daysInMonth[month - 1] || hour > 23 || minute > 59 || second > 59) {\n\t\treturn null;\n\t}\n\n\tconst date = new Date(value);\n\treturn Number.isNaN(date.getTime()) ? null : date;\n}\n\n/** The SDK's `iteration`, by the same path rule as {@link toWorkItemIterations}. */\nfunction toAzureWorkItemIteration(path: unknown): ProviderIssue['iteration'] {\n\tconst [iteration] = toWorkItemIterations(typeof path === 'string' ? path : undefined) ?? [];\n\treturn iteration != null ? { path: iteration.id, name: iteration.name } : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"models.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/models.ts"],"sourcesContent":["import type { IssueIteration, IssueMember } from '@gitlens/git/models/issue.js';\nimport { Issue, RepositoryAccessLevel } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequestState } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequestMember, PullRequestReviewer } from '@gitlens/git/models/pullRequest.js';\nimport {\n\tPullRequest,\n\tPullRequestMergeableState,\n\tPullRequestReviewDecision,\n\tPullRequestReviewState,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport type { ProviderAccount, ProviderIssue } from '../models.js';\n\nexport const vstsHostnameSuffix = '.visualstudio.com';\n\nexport interface AzureRepositoryDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n\tproject?: string;\n\tvirtualDirectory?: string;\n}\n\nexport interface AzureOrganizationDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n}\n\nexport interface AzureProjectDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n\tresourceId: string;\n\tresourceName: string;\n}\n\nexport interface AzureRemoteRepositoryDescriptor extends ResourceDescriptor {\n\tid: string;\n\tnodeId?: string;\n\tresourceName: string;\n\tname: string;\n\tprojectName?: string;\n\turl?: string;\n\tcloneUrlHttps?: string;\n\tcloneUrlSsh?: string;\n}\n\nexport interface AzureProjectInputDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n}\n\nexport type AzureWorkItemStateCategory = 'Proposed' | 'InProgress' | 'Resolved' | 'Completed' | 'Removed';\n\nexport function isClosedAzureWorkItemStateCategory(category: AzureWorkItemStateCategory | undefined): boolean {\n\treturn category === 'Completed' || category === 'Resolved' || category === 'Removed';\n}\n\nexport function azureWorkItemsStateCategoryToState(\n\tcategory: AzureWorkItemStateCategory | undefined,\n): IssueOrPullRequestState {\n\tswitch (category) {\n\t\tcase 'Resolved':\n\t\tcase 'Completed':\n\t\tcase 'Removed':\n\t\t\treturn 'closed';\n\t\tcase 'Proposed':\n\t\tcase 'InProgress':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\n\nexport interface AzureLink {\n\thref: string;\n}\n\nexport interface AzureUser {\n\tdisplayName: string;\n\turl: string;\n\t_links: {\n\t\tavatar: AzureLink;\n\t};\n\tid: string;\n\tuniqueName: string;\n\timageUrl: string;\n\tdescriptor?: string;\n}\n\nexport interface AzureUserWithVote extends AzureUser {\n\tisFlagged?: boolean;\n\thasDeclined?: boolean;\n\tisReapprove?: boolean;\n\tisRequired?: boolean;\n\tvote?: AzurePullRequestVote;\n}\n\nexport type AzurePullRequestVote =\n\t| 10 // approved\n\t| 5 // approved with suggestions\n\t| 0 // no vote\n\t| -5 // waiting for author\n\t| -10; // rejected\n\nexport interface AzureWorkItemCommentVersionRef {\n\tcommentId: number;\n\tcreatedInRevision: number;\n\tisDeleted: boolean;\n\ttext: string;\n\turl: string;\n\tversion: number;\n}\n\nexport interface AzureWorkItemRelation {\n\tattributes: {\n\t\t[key: string]: string;\n\t};\n\trelation: string;\n\turl: string;\n}\n\nexport interface WorkItem {\n\t_links: {\n\t\tfields: AzureLink;\n\t\thtml: AzureLink;\n\t\tself: AzureLink;\n\t\tworkItemComments: AzureLink;\n\t\tworkItemRevisions: AzureLink;\n\t\tworkItemType: AzureLink;\n\t\tworkItemUpdates: AzureLink;\n\t};\n\tfields: {\n\t\t//'System.AreaPath': string;\n\t\t'System.TeamProject': string;\n\t\t'System.IterationPath'?: string;\n\t\t'System.WorkItemType': string;\n\t\t'System.State': string;\n\t\t// 'System.Reason': string;\n\t\t'System.AssignedTo': AzureUser;\n\t\t'System.CreatedDate': string;\n\t\t'System.CreatedBy': AzureUser;\n\t\t'System.ChangedDate': string;\n\t\t'System.ChangedBy': AzureUser;\n\t\t'System.CommentCount': number;\n\t\t'System.Description': string;\n\t\t'System.Title': string;\n\t\t'Microsoft.VSTS.Common.ClosedDate': string;\n\t\t// 'Microsoft.VSTS.Common.StateChangeDate': string;\n\t\t// 'Microsoft.VSTS.Common.Priority': number;\n\t\t// 'Microsoft.VSTS.Common.Severity': string;\n\t\t// 'Microsoft.VSTS.Common.ValueArea': string;\n\t};\n\tid: number;\n\trev: number;\n\turl: string;\n\tcommentVersionRef?: AzureWorkItemCommentVersionRef;\n\trelations?: AzureWorkItemRelation[];\n}\n\nexport interface AzureWorkItemState {\n\tname: string;\n\tcolor: string;\n\tcategory: AzureWorkItemStateCategory;\n}\n\nexport type AzurePullRequestStatus = 'abandoned' | 'active' | 'completed' | 'notSet';\nexport function azurePullRequestStatusToState(status: AzurePullRequestStatus): IssueOrPullRequestState {\n\tswitch (status) {\n\t\tcase 'abandoned':\n\t\t\treturn 'closed';\n\t\tcase 'completed':\n\t\t\treturn 'merged';\n\t\tcase 'active':\n\t\tcase 'notSet':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\nexport function isClosedAzurePullRequestStatus(status: AzurePullRequestStatus): boolean {\n\treturn azurePullRequestStatusToState(status) !== 'opened';\n}\n\nexport type AzureProjectState = 'createPending' | 'deleted' | 'deleting' | 'new' | 'unchanged' | 'wellFormed';\nexport type AzureProjectVisibility = 'private' | 'public';\n\nexport interface AzureProject {\n\tid: string;\n\tname: string;\n\turl: string;\n\tstate: AzureProjectState;\n\trevision: number;\n\tvisibility: AzureProjectVisibility;\n\tlastUpdateTime: string;\n}\n\nexport interface AzureRepositoryReference {\n\tid: string;\n\tname: string;\n\turl: string;\n\tremoteUrl?: string;\n}\n\nexport interface AzureRepository extends AzureRepositoryReference {\n\tproject: AzureProject;\n\tsize: number;\n\tremoteUrl: string;\n\tsshUrl: string;\n\twebUrl: string;\n\tisDisabled: boolean;\n\tisInMaintenance: boolean;\n}\n\nexport interface AzurePullRequestRepository extends AzureRepositoryReference {\n\tproject: Pick<AzureProject, 'id' | 'name'>;\n}\n\n/** The URL fields of a repository response — all the fork lookup reads, and all an older `api-version` promises. */\nexport type AzureRepositoryUrls = Partial<Pick<AzureRepository, 'webUrl' | 'remoteUrl'>>;\n\n/** The `GET .../_apis/git/repositories/{repositoryId or name}` response, adding fork/default-branch fields to {@link AzureRepository}. */\nexport interface AzureRepositoryWithMetadata extends AzureRepository {\n\t/** Fully-qualified ref, e.g. `refs/heads/main`. */\n\tdefaultBranch?: string;\n\tisFork?: boolean;\n\tparentRepository?: {\n\t\tid: string;\n\t\tname?: string;\n\t\tproject?: { id: string; name: string };\n\t};\n}\n\nexport interface AzureGitUser {\n\tdate?: string;\n\temail?: string;\n\timageUrl?: string;\n\tname: string;\n}\n\nexport interface AzureGitCommitRef {\n\tcommitId: string;\n\turl: string;\n}\n\nexport interface AzureGitCommit {\n\t_links: {\n\t\tchanges: AzureLink;\n\t\trepository: AzureLink;\n\t\tself: AzureLink;\n\t\tweb: AzureLink;\n\t};\n\tauthor: AzureGitUser;\n\tcomment: string;\n\tcommentTruncated?: boolean;\n\tcommitId: string;\n\tcommitTooManyChanges?: boolean;\n\tcommitter: AzureGitUser;\n\tparents: string[];\n\tpush: {\n\t\tdate: string;\n\t\tpushedBy: AzureUser;\n\t\tpushId: number;\n\t};\n\tremoteUrl: string;\n\tstatuses?: AzureGitStatus[];\n\ttreeId: string;\n\turl: string;\n\tworkItems?: AzureResourceRef[];\n}\n\nexport interface AzureResourceRef {\n\tid: string;\n\turl: string;\n}\n\nexport interface AzurePullRequestCompletionOptions {\n\tautoCompleteIgnoreConflicts: number[];\n\tbypassPolicy: boolean;\n\tbypassReason: string;\n\tdeleteSourceBranch: boolean;\n\tmergeCommitMessage: string;\n\tmergeStrategy: 'noFastForward' | 'rebase' | 'rebaseMerge' | 'squash';\n\tsquashMerge: boolean;\n\ttransitionWorkItems: boolean;\n\ttriggeredByAutoComplete: boolean;\n}\n\nexport interface AzureGitStatus {\n\tcontext: {\n\t\tname: string;\n\t\tgenre: string;\n\t};\n\tcreatedBy: AzureUser;\n\tcreateDate: string;\n\tdescription: string;\n\tstate: 'error' | 'failed' | 'notApplicable' | 'notSet' | 'pending' | 'succeeded';\n\ttargetUrl: string;\n\tupdateDate: string;\n}\n\nexport interface AzureGitForkRef {\n\tcreator: AzureUser;\n\tisLocked: boolean;\n\tisLockedBy: AzureUser;\n\tname: string;\n\tobjectId: string;\n\tpeeledObjectId: string;\n\trepository: AzureRepositoryReference;\n\tstatuses: AzureGitStatus[];\n\turl: string;\n}\n\nexport interface AzureWebApiTagDefinition {\n\tactive: boolean;\n\tid: string;\n\tname: string;\n\turl: string;\n}\n\nexport interface AzureGitPullRequestMergeOptions {\n\tconflictAuthorshipCommits: boolean;\n\tdetectRenameFalsePositives: boolean;\n\tdisableRenames: boolean;\n}\n\nexport type AzurePullRequestAsyncStatus =\n\t| 'conflicts'\n\t| 'failure'\n\t| 'notSet'\n\t| 'queued'\n\t| 'rejectedByPolicy'\n\t| 'succeeded';\n\nexport interface AzurePullRequest {\n\trepository: AzurePullRequestRepository;\n\tpullRequestId: number;\n\tcodeReviewId: number;\n\tstatus: AzurePullRequestStatus;\n\tcreatedBy: AzureUser;\n\tcreationDate: string;\n\tclosedDate?: string;\n\tclosedBy?: AzureUser; // Can be missed even if closedDate is presented.\n\ttitle: string;\n\tdescription: string;\n\tforkSource?: AzureGitForkRef;\n\tsourceRefName: string;\n\ttargetRefName: string;\n\tisDraft: boolean;\n\tmergeId: string;\n\tmergeStatus?: AzurePullRequestAsyncStatus;\n\tlastMergeCommit?: AzureGitCommitRef;\n\tlastMergeSourceCommit: AzureGitCommitRef;\n\tlastMergeTargetCommit: AzureGitCommitRef;\n\treviewers: AzureUserWithVote[];\n\turl: string;\n\tsupportsIterations: boolean;\n}\n\nexport interface AzurePullRequestWithLinks extends AzurePullRequest {\n\t_links: {\n\t\tself: AzureLink;\n\t\trepository: AzureLink;\n\t\tworkItems: AzureLink;\n\t\tsourceBranch: AzureLink;\n\t\ttargetBranch: AzureLink;\n\t\tstatuses: AzureLink;\n\t\tsourceCommit: AzureLink;\n\t\ttargetCommit: AzureLink;\n\t\tcreatedBy: AzureLink;\n\t\titerations: AzureLink;\n\t};\n\tartifactId: string;\n\tautoCompleteSetBy?: AzureUser;\n\tcommits?: AzureGitCommitRef[];\n\tcompletionOptions?: AzurePullRequestCompletionOptions;\n\tcompletionQueueTime?: string;\n\thasMultipleMergeBases?: boolean;\n\tlabels?: AzureWebApiTagDefinition[];\n\tmergeFailureMessage?: string;\n\tmergeFailureType?: 'caseSensitive' | 'none' | 'objectTooLarge' | 'unknown';\n\tmergeOptions?: AzureGitPullRequestMergeOptions;\n\tremoteUrl?: string;\n\tworkItemRefs?: AzureResourceRef[];\n}\n\nexport function getAzureDevOpsOwner(url: URL): string {\n\treturn url.pathname.split('/')[1];\n}\nexport function isVsts(domain: string): boolean {\n\treturn domain.endsWith(vstsHostnameSuffix);\n}\n\n/**\n * Encodes one route segment of an Azure DevOps request (#5878): `encodeURIComponent` leaves a dot segment as is, and\n * the URL would resolve it away, re-scoping the request instead of failing it.\n */\nexport function encodeAzurePathSegment(value: string): string {\n\tif (value === '.' || value === '..') throw new Error(`Invalid Azure path segment '${value}'.`);\n\n\treturn encodeURIComponent(value);\n}\n\n/**\n * `baseUrl` and `owner` are the authoritative prefix used for the API request. The payload URL cannot supply that\n * prefix safely because it may address the repository by id or name an untrusted host.\n */\nfunction getAzureRepositoryWebUrl(baseUrl: string, owner: string, projectName: string, repoName: string): string {\n\tconst repoPath = `${encodeURIComponent(owner)}/${encodeURIComponent(projectName)}/_git/${encodeURIComponent(repoName)}`;\n\treturn `${baseUrl.replace(/\\/+$/, '')}/${repoPath}`;\n}\n\nexport function getAzurePullRequestWebUrl(pr: AzurePullRequest, baseUrl: string, owner: string): string {\n\tconst repoUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\treturn `${repoUrl}/pullrequest/${pr.pullRequestId}`;\n}\n\n/**\n * Whether `url` and `expected` are the two cloud spellings of one organization: a cloud organization answers on both\n * `dev.azure.com/{owner}` and the legacy `{owner}.visualstudio.com`, so those name the same host. Anything else is\n * somewhere we didn't ask.\n */\nfunction isSameAzureCloudOrganization(url: URL, expected: URL, expectedOwner: string): boolean {\n\tif (url.protocol !== expected.protocol) return false;\n\n\tconst owner = expectedOwner.toLowerCase();\n\tif (expected.hostname !== 'dev.azure.com' && !isVsts(expected.hostname)) return false;\n\n\tif (url.hostname === 'dev.azure.com') return getAzureDevOpsOwner(url).toLowerCase() === owner;\n\n\treturn url.hostname === `${owner}${vstsHostnameSuffix}`;\n}\n\n/**\n * Whether `url` sits under the collection the integration is configured to talk to — `{expectedUrl}/{expectedOwner}`.\n * A matching origin says nothing on its own: Azure organizations and self-hosted collections are path segments.\n */\nfunction isUnderAzureCollection(url: URL, expected: URL, expectedOwner: string): boolean {\n\tconst base = expected.pathname.replace(/\\/+$/, '');\n\tconst prefix = `${base}/${encodeURIComponent(expectedOwner)}/`.toLowerCase();\n\treturn `${url.pathname}/`.toLowerCase().startsWith(prefix);\n}\n\n/**\n * Restricts a provider-supplied repository URL to the integration's collection and removes credentials, query, and\n * fragment before a consumer can use it as a git remote. Takes the value as the payloads carry it — every one of\n * these URLs is optional — so a caller never has to spell the absent case itself.\n */\nexport function sanitizeAzureRepositoryUrl(\n\tvalue: string | undefined,\n\texpectedUrl: string,\n\texpectedOwner: string,\n): string | undefined {\n\tif (value == null) return undefined;\n\n\tlet expected: URL;\n\tlet url: URL;\n\ttry {\n\t\texpected = new URL(expectedUrl);\n\t\turl = new URL(value);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (expected.protocol !== 'https:' && expected.protocol !== 'http:') return undefined;\n\tif (!expectedOwner) return undefined;\n\n\tif (url.origin === expected.origin) {\n\t\tif (!isUnderAzureCollection(url, expected, expectedOwner)) return undefined;\n\t} else if (!isSameAzureCloudOrganization(url, expected, expectedOwner)) {\n\t\treturn undefined;\n\t}\n\n\turl.username = '';\n\turl.password = '';\n\turl.search = '';\n\turl.hash = '';\n\treturn url.toString();\n}\n\n/**\n * The clone URL the payload carries for a repository, restricted to the integration's collection and then\n * cross-checked against the repository that payload names.\n *\n * The collection check alone is not enough for a URL that is READ rather than built. Every other repository URL the\n * model reports is built from the authoritative prefix, so the payload cannot move it; this one cannot be built,\n * because Azure spells a repository for git the same way it spells it for the web only by convention. A URL under\n * the right collection may still name a DIFFERENT repository in it, which would leave `cloneHttps` describing one\n * repository while `url` describes another.\n *\n * Azure ends a clone URL with `_git/{repo}` on every host style — including the short `{owner}/_git/{repo}` form it\n * uses when a repository carries its project's name, and the legacy `{owner}.visualstudio.com` spelling that has no\n * organization segment at all — so the repository name is the one part comparable across all of them. A same-named\n * repository in another project of the same organization is the single substitution this cannot catch.\n */\nfunction getAzureRepositoryCloneUrl(\n\trepository: AzureRepositoryReference,\n\tbaseUrl: string,\n\towner: string,\n): string | undefined {\n\tconst sanitized = sanitizeAzureRepositoryUrl(repository.remoteUrl, baseUrl, owner);\n\tif (sanitized == null) return undefined;\n\n\t// Safe to parse: `sanitizeAzureRepositoryUrl` returns what it already parsed.\n\tconst segments = new URL(sanitized).pathname.split('/').filter(s => s.length > 0);\n\tconst name = segments.pop();\n\tif (name == null || segments.pop() !== '_git') return undefined;\n\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(name);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\t// Azure resolves project and repository names case-insensitively, so comparing them any other way would refuse a\n\t// URL the provider considers the same one.\n\treturn decoded.toLowerCase() === repository.name.toLowerCase() ? sanitized : undefined;\n}\n\nexport function fromAzurePullRequestMergeStatusToMergeableState(\n\tmergeStatus: AzurePullRequestAsyncStatus,\n): PullRequestMergeableState {\n\tswitch (mergeStatus) {\n\t\tcase 'conflicts':\n\t\t\treturn PullRequestMergeableState.Conflicting;\n\t\tcase 'failure':\n\t\t\treturn PullRequestMergeableState.FailingChecks;\n\t\tcase 'rejectedByPolicy':\n\t\t\treturn PullRequestMergeableState.BlockedByPolicy;\n\t\tcase 'succeeded':\n\t\t\treturn PullRequestMergeableState.Mergeable;\n\t\tcase 'notSet':\n\t\tcase 'queued':\n\t\tdefault:\n\t\t\treturn PullRequestMergeableState.Unknown;\n\t}\n}\n\nexport function fromAzurePullRequestVoteToReviewState(vote: AzurePullRequestVote): PullRequestReviewState {\n\tswitch (vote) {\n\t\tcase 10:\n\t\tcase 5:\n\t\t\treturn PullRequestReviewState.Approved;\n\t\tcase 0:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t\tcase -5:\n\t\tcase -10:\n\t\t\treturn PullRequestReviewState.ChangesRequested;\n\t\tdefault:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t}\n}\n\nexport function fromAzureUserWithVoteToReviewer(reviewer: AzureUserWithVote): PullRequestReviewer {\n\treturn {\n\t\tisCodeOwner: undefined,\n\t\treviewer: {\n\t\t\tavatarUrl: reviewer.imageUrl,\n\t\t\tid: reviewer.id,\n\t\t\tname: reviewer.displayName,\n\t\t\tusername: reviewer.uniqueName,\n\t\t\turl: reviewer.url,\n\t\t},\n\t\tstate: fromAzurePullRequestVoteToReviewState(reviewer.vote ?? 0),\n\t};\n}\n\nexport function getAzurePullRequestReviewDecision(\n\tvotes: AzurePullRequestVote[],\n): PullRequestReviewDecision | undefined {\n\tconst reviewStates = votes.map(vote => fromAzurePullRequestVoteToReviewState(vote));\n\tif (reviewStates.includes(PullRequestReviewState.ChangesRequested)) {\n\t\treturn PullRequestReviewDecision.ChangesRequested;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.ReviewRequested)) {\n\t\treturn PullRequestReviewDecision.ReviewRequired;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.Approved)) {\n\t\treturn PullRequestReviewDecision.Approved;\n\t}\n\n\treturn undefined;\n}\n\nexport function fromAzureReviewerToPullRequestMember(reviewer: AzureUser): PullRequestMember {\n\treturn {\n\t\tavatarUrl: reviewer.imageUrl,\n\t\tid: reviewer.id,\n\t\tname: reviewer.displayName,\n\t\tusername: reviewer.uniqueName,\n\t\turl: reviewer.url,\n\t};\n}\n\nexport function normalizeAzureBranchName(branchName: string): string {\n\treturn branchName.startsWith('refs/heads/') ? branchName.replace('refs/heads/', '') : branchName;\n}\n\nfunction fromAzureUserToMember(user: AzureUser, type: 'issue'): IssueMember;\nfunction fromAzureUserToMember(user: AzureUser, type: 'pullRequest'): PullRequestMember;\nfunction fromAzureUserToMember(user: AzureUser, _type: 'issue' | 'pullRequest'): PullRequestMember | IssueMember {\n\treturn {\n\t\tavatarUrl: user.imageUrl,\n\t\tid: user.id,\n\t\tname: user.displayName,\n\t\tusername: user.uniqueName,\n\t\turl: user.url,\n\t};\n}\n\n/**\n * The URLs of a cross-repository pull request's fork, resolved by a lookup because the pull request payload embeds\n * only an abbreviated reference to it: `url` is the URL the head ref reports — the fork's web URL, or its clone URL\n * when the response carries no web URL — and `cloneHttps` its HTTPS clone URL.\n *\n * A fork that resolves neither is reported as no fork at all rather than as an object with nothing in it, so `url`\n * is always present here; `cloneHttps` alone is best-effort, and is absent when the response omits it or its URL\n * sits outside the integration's collection.\n */\nexport interface AzureForkRepositoryUrls {\n\turl: string;\n\tcloneHttps: string | undefined;\n}\n\n/**\n * `baseUrl` and `owner` are the prefix the pull request was read through; every URL the model reports is built from\n * them. The required `forkRepositoryUrls` argument makes each call site resolve the best-effort fork URLs explicitly.\n */\nexport function fromAzurePullRequest(\n\tpr: AzurePullRequest,\n\tprovider: Provider,\n\towner: string,\n\tbaseUrl: string,\n\tforkRepositoryUrls: AzureForkRepositoryUrls | undefined,\n): PullRequest {\n\tconst baseRepositoryUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\tconst baseCloneHttps = getAzureRepositoryCloneUrl(pr.repository, baseUrl, owner);\n\n\tconst forkRepository = pr.forkSource?.repository;\n\tconst headRepositoryUrl = forkRepository == null ? baseRepositoryUrl : forkRepositoryUrls?.url;\n\tconst headCloneHttps = forkRepository == null ? baseCloneHttps : forkRepositoryUrls?.cloneHttps;\n\n\treturn new PullRequest(\n\t\tprovider,\n\t\tfromAzureUserToMember(pr.createdBy, 'pullRequest'),\n\t\tpr.pullRequestId.toString(),\n\t\tpr.pullRequestId.toString(),\n\t\tpr.title,\n\t\tgetAzurePullRequestWebUrl(pr, baseUrl, owner),\n\t\t{\n\t\t\towner: owner,\n\t\t\trepo: pr.repository.name,\n\t\t\tid: pr.repository.id,\n\t\t\t// TODO: Remove this assumption once actual access level is available\n\t\t\taccessLevel: RepositoryAccessLevel.Write,\n\t\t},\n\t\tazurePullRequestStatusToState(pr.status),\n\t\tnew Date(pr.creationDate),\n\t\tnew Date(pr.closedDate || pr.creationDate),\n\t\tpr.closedDate ? new Date(pr.closedDate) : undefined,\n\t\tpr.closedDate && pr.status === 'completed' ? new Date(pr.closedDate) : undefined,\n\t\tfromAzurePullRequestMergeStatusToMergeableState(pr.mergeStatus ?? 'notSet'),\n\t\tundefined,\n\t\t{\n\t\t\tbase: {\n\t\t\t\tbranch: pr.targetRefName ? normalizeAzureBranchName(pr.targetRefName) : '',\n\t\t\t\tsha: pr.lastMergeTargetCommit?.commitId ?? '',\n\t\t\t\trepo: pr.repository.name,\n\t\t\t\towner: owner,\n\t\t\t\texists: pr.targetRefName != null,\n\t\t\t\turl: baseRepositoryUrl,\n\t\t\t\tcloneHttps: baseCloneHttps,\n\t\t\t},\n\t\t\thead: {\n\t\t\t\tbranch: pr.sourceRefName ? normalizeAzureBranchName(pr.sourceRefName) : '',\n\t\t\t\tsha: pr.lastMergeSourceCommit?.commitId ?? '',\n\t\t\t\trepo: forkRepository?.name ?? pr.repository.name,\n\t\t\t\towner: owner,\n\t\t\t\texists: pr.sourceRefName != null,\n\t\t\t\turl: headRepositoryUrl,\n\t\t\t\tcloneHttps: headCloneHttps,\n\t\t\t},\n\t\t\tisCrossRepository: pr.forkSource != null,\n\t\t},\n\t\tpr.isDraft,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tgetAzurePullRequestReviewDecision(pr.reviewers?.filter(r => r.isRequired).map(r => r.vote ?? 0) ?? []),\n\t\tpr.reviewers.filter(r => r.vote == null || r.vote === 0).map(r => fromAzureUserWithVoteToReviewer(r)),\n\t\tpr.reviewers.filter(r => r.vote != null && r.vote !== 0).map(r => fromAzureUserWithVoteToReviewer(r)),\n\t\tpr.reviewers.map(r => fromAzureReviewerToPullRequestMember(r)),\n\t\tundefined,\n\t\t{\n\t\t\tid: pr.repository?.project?.id,\n\t\t\tname: pr.repository.project.name,\n\t\t\tresourceId: '', // TODO: This is a workaround until we can get the org id here.\n\t\t\tresourceName: owner,\n\t\t},\n\t);\n}\n\nexport function fromAzureWorkItem(\n\tworkItem: WorkItem,\n\tprovider: Provider,\n\tproject: AzureProjectDescriptor,\n\tstateCategory?: AzureWorkItemStateCategory,\n): Issue {\n\treturn new Issue(\n\t\tprovider,\n\t\tworkItem.id.toString(),\n\t\tworkItem.id.toString(),\n\t\tworkItem.fields['System.Title'],\n\t\tworkItem._links.html.href,\n\t\tnew Date(workItem.fields['System.CreatedDate']),\n\t\tnew Date(workItem.fields['System.ChangedDate']),\n\t\tisClosedAzureWorkItemStateCategory(stateCategory),\n\t\tazureWorkItemsStateCategoryToState(stateCategory),\n\t\tfromAzureUserToMember(workItem.fields['System.CreatedBy'], 'issue'),\n\t\tworkItem.fields['System.AssignedTo'] != null\n\t\t\t? [fromAzureUserToMember(workItem.fields['System.AssignedTo'], 'issue')]\n\t\t\t: [],\n\t\tundefined,\n\t\tworkItem.fields['Microsoft.VSTS.Common.ClosedDate']\n\t\t\t? new Date(workItem.fields['Microsoft.VSTS.Common.ClosedDate'])\n\t\t\t: undefined,\n\t\tundefined,\n\t\tworkItem.fields['System.CommentCount'],\n\t\tundefined,\n\t\tworkItem.fields['System.Description'],\n\t\tproject,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\ttoWorkItemIterations(workItem.fields['System.IterationPath']),\n\t);\n}\n\n/**\n * Azure reports the iteration as a backslash-delimited path rooted at the project. The project root is its default\n * for a work item with no sprint, so only a nested path names one; the path is the identity because Azure supplies\n * no iteration id here.\n *\n * Mirrors `normalizeIteration` in provider-apis so the same work item yields the same identity whether it is read\n * here or through the SDK. That contract keeps the path **verbatim** — it is what the iteration is matched back by,\n * so it is deliberately not trimmed — and trims only the display name.\n */\nfunction toWorkItemIterations(path: string | undefined): IssueIteration[] | undefined {\n\tif (!path) return undefined;\n\n\tconst segments = path.split('\\\\');\n\tif (segments.length < 2) return undefined;\n\n\tconst name = segments.at(-1)?.trim();\n\treturn name ? [{ id: path, name: name }] : undefined;\n}\n\n/**\n * A work item as `GET …/_apis/wit/workitems/{id}?$expand=Links` returns it. Loosely typed because\n * {@link fromAzureWorkItemToProviderIssue} checks every field it reads, as provider-apis does.\n */\nexport interface AzureWorkItemResponse {\n\tid?: unknown;\n\tfields?: Record<string, unknown>;\n\t_links?: { html?: { href?: unknown } };\n}\n\n/**\n * Converts a work item exactly as provider-apis' `getIssuesForAzureProject` converts each row it reads, so a work\n * item read by id reaches `toIssueShape` identical to the list read's row for it. provider-apis exports neither\n * that converter nor a single work item read, so this mirrors it field for field; the batch issue tests compare\n * the two, so a provider-apis change that this misses fails there.\n *\n * `undefined` for a work item the SDK would skip: no positive integer id, no title, or no valid created date.\n * `state.name` is the raw `System.State`, as on every list read here, which passes the SDK no state-name map.\n */\nexport function fromAzureWorkItemToProviderIssue(\n\tworkItem: AzureWorkItemResponse,\n\tnamespace: string,\n\tproject: string,\n): ProviderIssue | undefined {\n\tconst fields = workItem.fields;\n\tconst id = workItem.id;\n\tconst title = fields?.['System.Title'];\n\tconst createdDate = parseAzureWorkItemDate(fields?.['System.CreatedDate']);\n\tif (\n\t\tfields == null ||\n\t\ttypeof id !== 'number' ||\n\t\t!Number.isSafeInteger(id) ||\n\t\tid <= 0 ||\n\t\ttypeof title !== 'string' ||\n\t\t!title.trim() ||\n\t\tcreatedDate == null\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst assignee = fromAzureWorkItemIdentity(fields['System.AssignedTo']);\n\tconst commentCount = fields['System.CommentCount'];\n\tconst url = workItem._links?.html?.href;\n\tconst tags = fields['System.Tags'];\n\tconst state = fields['System.State'];\n\tconst type = fields['System.WorkItemType'];\n\tconst description = fields['System.Description'];\n\treturn {\n\t\tid: id.toString(),\n\t\tnumber: id.toString(),\n\t\ttitle: title,\n\t\tcommentCount: typeof commentCount === 'number' && Number.isFinite(commentCount) ? commentCount : null,\n\t\tauthor: fromAzureWorkItemIdentity(fields['System.CreatedBy']),\n\t\tclosedDate: parseAzureWorkItemDate(fields['Microsoft.VSTS.Common.ClosedDate']),\n\t\tcreatedDate: createdDate,\n\t\tupdatedDate: parseAzureWorkItemDate(fields['System.ChangedDate']),\n\t\turl: typeof url === 'string' ? url : null,\n\t\tassignees: assignee != null ? [assignee] : [],\n\t\tdescription: typeof description === 'string' ? description : null,\n\t\tstate: typeof state === 'string' && state ? { name: state, color: null } : null,\n\t\ttype: typeof type === 'string' ? type : null,\n\t\titeration: toAzureWorkItemIteration(fields['System.IterationPath']),\n\t\trepository: null,\n\t\tproject: { namespace: namespace, name: project, resourceId: null, key: null, id: null },\n\t\tupvoteCount: 0,\n\t\tlabels: (typeof tags === 'string' ? tags.split(';') : []).map(tag => ({\n\t\t\tcolor: null,\n\t\t\tdescription: null,\n\t\t\tid: null,\n\t\t\tname: tag.trim(),\n\t\t})),\n\t};\n}\n\n/** An identity field, as provider-apis maps it: `name` from `uniqueName`, `username` from `displayName`. */\nfunction fromAzureWorkItemIdentity(value: unknown): ProviderAccount | null {\n\tconst identity = value as\n\t\t| { id?: unknown; uniqueName?: unknown; displayName?: unknown; _links?: { avatar?: { href?: unknown } } }\n\t\t| null\n\t\t| undefined;\n\tif (typeof identity?.id !== 'string' || !identity.id.trim()) return null;\n\n\tconst avatarUrl = identity._links?.avatar?.href;\n\treturn {\n\t\tavatarUrl: typeof avatarUrl === 'string' ? avatarUrl : null,\n\t\temail: null,\n\t\tid: identity.id,\n\t\tname: typeof identity.uniqueName === 'string' ? identity.uniqueName : null,\n\t\tusername: typeof identity.displayName === 'string' ? identity.displayName : null,\n\t\turl: null,\n\t};\n}\n\n/** Only a UTC ISO timestamp naming a real calendar date and time, as provider-apis accepts; anything else is `null`. */\nfunction parseAzureWorkItemDate(value: unknown): Date | null {\n\tif (typeof value !== 'string') return null;\n\n\tconst match = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,7})?Z$/.exec(value);\n\tif (match == null) return null;\n\n\tconst [year, month, day, hour, minute, second] = match.slice(1).map(Number);\n\tconst isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n\tconst daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\tif (month < 1 || month > 12 || day < 1 || day > daysInMonth[month - 1] || hour > 23 || minute > 59 || second > 59) {\n\t\treturn null;\n\t}\n\n\tconst date = new Date(value);\n\treturn Number.isNaN(date.getTime()) ? null : date;\n}\n\n/** The SDK's `iteration`, by the same path rule as {@link toWorkItemIterations}. */\nfunction toAzureWorkItemIteration(path: unknown): ProviderIssue['iteration'] {\n\tconst [iteration] = toWorkItemIterations(typeof path === 'string' ? path : undefined) ?? [];\n\treturn iteration != null ? { path: iteration.id, name: iteration.name } : undefined;\n}\n"],"mappings":"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"}