@gitkraken/core-gitlens 0.16.1 → 0.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/git/watching/classifyChange.js +5 -2
  3. package/dist/git/watching/classifyChange.js.map +1 -1
  4. package/dist/git/watching/watchGroup.js +2 -1
  5. package/dist/git/watching/watchGroup.js.map +1 -1
  6. package/dist/git-cli/providers/branches.d.ts +33 -9
  7. package/dist/git-cli/providers/branches.js +124 -41
  8. package/dist/git-cli/providers/branches.js.map +1 -1
  9. package/dist/plus/integrations/integrationService.d.ts +1 -0
  10. package/dist/plus/integrations/integrationService.js +18 -5
  11. package/dist/plus/integrations/integrationService.js.map +1 -1
  12. package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
  13. package/dist/plus/integrations/providers/apiClients.js +12 -3
  14. package/dist/plus/integrations/providers/apiClients.js.map +1 -1
  15. package/dist/plus/integrations/providers/azure/azure.d.ts +13 -11
  16. package/dist/plus/integrations/providers/azure/azure.js +128 -47
  17. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  18. package/dist/plus/integrations/providers/azure/models.d.ts +4 -10
  19. package/dist/plus/integrations/providers/azure/models.js +25 -9
  20. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  21. package/dist/plus/integrations/providers/azure/workItemDescriptions.d.ts +10 -0
  22. package/dist/plus/integrations/providers/azure/workItemDescriptions.js +48 -0
  23. package/dist/plus/integrations/providers/azure/workItemDescriptions.js.map +1 -0
  24. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +20 -19
  25. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +65 -40
  26. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  27. package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
  28. package/dist/plus/integrations/providers/providerApiClient.d.ts +9 -0
  29. package/dist/plus/integrations/providers/providerApiClient.js +66 -0
  30. package/dist/plus/integrations/providers/providerApiClient.js.map +1 -0
  31. package/dist/plus/integrations/providers/providersApi.d.ts +10 -9
  32. package/dist/plus/integrations/providers/providersApi.js +163 -145
  33. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  34. package/dist/plus/integrations/reads/drains.d.ts +0 -1
  35. package/docs/integrations.md +30 -13
  36. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","names":["Issue","GitPullRequestState","GitPullRequestMergeableState","GitPullRequestReviewState"],"sources":["../../../../../../plus/integrations/src/providers/azure/models.ts"],"sourcesContent":["import type { AzureWorkItemHtmlField } from '@gitkraken/provider-apis';\nimport { getAzureWorkItemDescription } from '@gitkraken/provider-apis';\nimport type { IssueIteration, IssueMember, IssueProjection, IssueProviderState } from '@gitlens/git/models/issue.js';\nimport { Issue } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequestState } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequestMember, PullRequestProjection, 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 { isTerminalIssueStateCategory } from '@gitlens/git/utils/issue.utils.js';\nimport type { IssueEtagFields, PullRequestEtagFields, PullRequestEtagInclude } from '../../models/integration.js';\nimport { azureWorkItemDescriptionRule, pullRequestRevision } from '../../models/integration.js';\nimport type { ProviderAccount, ProviderIssue } from '../models.js';\nimport {\n\tfromProviderPullRequestMergeableState,\n\tfromProviderPullRequestState,\n\tGitPullRequestMergeableState,\n\tGitPullRequestReviewState,\n\tGitPullRequestState,\n\ttoIssueBodyField,\n} from '../models.js';\nimport type { ProviderPullRequestReview } from '../pullRequestReviews.js';\nimport {\n\tdecideProviderReviewDecision,\n\tfromPullRequestReviewDecision,\n\ttoCompletedReviews,\n\ttoReviewRequests,\n} from '../pullRequestReviews.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\n/**\n * The workflow category of an Azure DevOps state category, as a work item type's states endpoint reports it:\n * `Removed` is abandoned work, so `CANCELED`, kept apart from the `DONE` of `Completed` and `Resolved`.\n * `undefined` for a category Azure doesn't document, which then neither opens nor closes the work item.\n */\nexport function azureIssueStateCategory(\n\tcategory: AzureWorkItemStateCategory | undefined,\n): IssueProviderState['category'] {\n\tswitch (category) {\n\t\tcase 'Removed':\n\t\t\treturn 'CANCELED';\n\t\tcase 'Completed':\n\t\tcase 'Resolved':\n\t\t\treturn 'DONE';\n\t\tcase 'InProgress':\n\t\t\treturn 'IN_PROGRESS';\n\t\tcase 'Proposed':\n\t\t\treturn 'TO_DO';\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function isClosedAzureWorkItemStateCategory(category: AzureWorkItemStateCategory | undefined): boolean {\n\treturn isTerminalIssueStateCategory(azureIssueStateCategory(category));\n}\n\nexport function azureWorkItemsStateCategoryToState(\n\tcategory: AzureWorkItemStateCategory | undefined,\n\tclosedDate?: Date,\n): IssueOrPullRequestState | undefined {\n\tif (azureIssueStateCategory(category) != null) {\n\t\treturn isClosedAzureWorkItemStateCategory(category) ? 'closed' : 'opened';\n\t}\n\n\treturn closedDate != null ? 'closed' : undefined;\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\t// Every other field the work item has a value for, its rich text description fields among them.\n\t\t[field: string]: unknown;\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.toLowerCase().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\tprojection?: PullRequestProjection,\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},\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\t// The read selects the merge status, so an absent one is Azure's own \"not set\", as provider-apis maps it.\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\tundefined, // version\n\t\tundefined, // commitCount\n\t\tundefined, // stack\n\t\tundefined, // filesChanged\n\t\tundefined, // body\n\t\tundefined, // number\n\t\tundefined, // authoredByMe\n\t\tprojection,\n\t);\n}\n\nexport function fromAzureWorkItem(\n\tworkItem: WorkItem,\n\tprovider: Provider,\n\tproject: AzureProjectDescriptor,\n\tstateCategory?: AzureWorkItemStateCategory,\n\tprojection?: IssueProjection,\n\thtmlFields?: readonly AzureWorkItemHtmlField[],\n): Issue {\n\tconst state = workItem.fields['System.State'];\n\tconst description = getAzureWorkItemDescription(workItem.fields, htmlFields);\n\tconst closedDate = parseAzureWorkItemDate(workItem.fields['Microsoft.VSTS.Common.ClosedDate']) ?? undefined;\n\tconst normalizedState = azureWorkItemsStateCategoryToState(stateCategory, closedDate);\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\tnormalizedState === 'closed',\n\t\tnormalizedState,\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\tclosedDate,\n\t\tundefined,\n\t\tworkItem.fields['System.CommentCount'],\n\t\tundefined,\n\t\tdescription?.value,\n\t\tproject,\n\t\tundefined,\n\t\tworkItem.fields['System.WorkItemType'],\n\t\ttypeof state === 'string' && state\n\t\t\t? { name: state, category: azureIssueStateCategory(stateCategory) }\n\t\t\t: undefined,\n\t\tundefined,\n\t\ttoWorkItemIterations(workItem.fields['System.IterationPath']),\n\t\tprojection,\n\t\tdescription != null ? toIssueBodyField(description.field) : undefined,\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 * A cheap read's work item with the workflow category its state classifies to (see\n * {@link toAzureWorkItemEtagFields}). Beside the work item rather than on it, so the wire type stays what Azure sends.\n */\nexport interface AzureWorkItemEtagRead {\n\treadonly workItem: AzureWorkItemResponse;\n\treadonly category: IssueProviderState['category'];\n}\n\n/**\n * Converts a work item as provider-apis' `getIssuesForAzureProject` converts each row it reads, so a work item read\n * by id reaches `toIssueShape` identical to the list read's row for it. provider-apis exports neither that converter\n * nor a single work item read, so this mirrors it field for field; the batch issue tests compare the two, so a\n * 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. Like\n * the SDK's row, the state carries no workflow category: `ProvidersApi` classifies both reads' rows the same way\n * afterwards, from the work item type's states.\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\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\t...toAzureWorkItemDescriptionFields(fields),\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\t// provider-apis fills 0, but work items have no reactions.\n\t\tupvoteCount: null,\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/**\n * A work item's `description` and `descriptionField`, as provider-apis' list rows carry them: `descriptionField` only\n * when there is a description. A catalog only adds fields to try, so this pair read with `htmlFields` can replace a\n * row's pair read without them.\n */\nexport function toAzureWorkItemDescriptionFields(\n\tfields: Readonly<Record<string, unknown>> | null | undefined,\n\thtmlFields?: readonly AzureWorkItemHtmlField[],\n): Pick<ProviderIssue, 'description' | 'descriptionField'> {\n\tconst description = getAzureWorkItemDescription(fields, htmlFields);\n\treturn {\n\t\tdescription: description?.value ?? null,\n\t\t...(description != null ? { descriptionField: description.field } : {}),\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`. */\nexport function 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\n/**\n * The most ids one cheap work item read asks for: the most Azure DevOps' work item list (`GET …/_apis/wit/workitems`)\n * takes.\n */\nexport const azureWorkItemsEtagFieldsMaxIds = 200;\n\n/**\n * The fields the cheap work item read selects: only the ones a full row's etag reads, and the project the full read\n * checks the work item against. The state and type let the cheap read resolve the same workflow category as the full\n * read, from the same work item type states.\n */\nexport const azureWorkItemEtagFieldNames: readonly string[] = [\n\t'System.TeamProject',\n\t'System.State',\n\t'System.WorkItemType',\n\t'System.ChangedDate',\n\t'Microsoft.VSTS.Common.ClosedDate',\n];\n\n/**\n * A cheap read's work item, as the fields its full batch row's etag reads: `toIssueShape` closes the row the full\n * read builds (see {@link fromAzureWorkItemToProviderIssue}) from its known workflow category, falling back to a\n * closed date only when the category is unavailable. A historical date must not close a reopened work item.\n * Throws where the full read fails the work item: on one in another project than `project` (compared as Azure\n * compares names), and on a missing or unparseable change date, so the target falls through to the full read.\n */\nexport function toAzureWorkItemEtagFields(\n\tworkItem: AzureWorkItemResponse,\n\tproject: string,\n\tcategory: IssueProviderState['category'],\n): IssueEtagFields {\n\tconst fields = workItem.fields;\n\tconst teamProject = fields?.['System.TeamProject'];\n\tif (typeof teamProject === 'string' && teamProject.toLowerCase() !== project.toLowerCase()) {\n\t\tthrow new Error(\n\t\t\t`Azure DevOps work item ${String(workItem.id)} is in project '${teamProject}', not '${project}'`,\n\t\t);\n\t}\n\n\tconst updatedDate = parseAzureWorkItemDate(fields?.['System.ChangedDate']);\n\tif (updatedDate == null) {\n\t\tthrow new Error(`Azure DevOps returned work item ${String(workItem.id)} without a change date`);\n\t}\n\n\tconst state =\n\t\tcategory != null\n\t\t\t? isTerminalIssueStateCategory(category)\n\t\t\t\t? 'closed'\n\t\t\t\t: 'opened'\n\t\t\t: parseAzureWorkItemDate(fields?.['Microsoft.VSTS.Common.ClosedDate']) != null\n\t\t\t\t? 'closed'\n\t\t\t\t: undefined;\n\treturn {\n\t\tstate: state,\n\t\tupdatedDate: updatedDate,\n\t\tdescriptionRule: azureWorkItemDescriptionRule,\n\t};\n}\n\ntype ProviderPullRequestState = Parameters<typeof fromProviderPullRequestState>[0];\ntype ProviderMergeableState = keyof typeof fromProviderPullRequestMergeableState;\ntype ProviderReviewState = keyof typeof fromPullRequestReviewDecision;\n\n// The tables below mirror provider-apis' (0.61.0) Azure DevOps pull request mapping behind `getPullRequestForRepo` (`Xe`\n// and its helpers in the bundle), which it doesn't export. A full batch row takes these values on through\n// `fromProviderPullRequest`, so the cheap check composes the same steps to compute the same etag.\n\n/** provider-apis' pull request `status` map (`bi`). It has no `notSet`, which therefore maps to `undefined`. */\nconst azurePullRequestProviderStates: Partial<Record<string, ProviderPullRequestState>> = {\n\tactive: GitPullRequestState.Open,\n\tcompleted: GitPullRequestState.Merged,\n\tabandoned: GitPullRequestState.Closed,\n};\n\n/** provider-apis' `mergeStatus` map (`Ti`); any other status, or none, is unknown. */\nconst azureMergeStatusProviderStates: Partial<Record<string, ProviderMergeableState>> = {\n\tconflicts: GitPullRequestMergeableState.Conflicts,\n\tfailure: GitPullRequestMergeableState.FailingChecks,\n\trejectedByPolicy: GitPullRequestMergeableState.Blocked,\n\tsucceeded: GitPullRequestMergeableState.Mergeable,\n};\n\n/** provider-apis' reviewer `vote` map (`Di`); any other vote reads as review requested. */\nconst azureVoteProviderReviewStates: Partial<Record<number, ProviderReviewState>> = {\n\t10: GitPullRequestReviewState.Approved,\n\t5: GitPullRequestReviewState.Approved,\n\t0: GitPullRequestReviewState.ReviewRequested,\n\t[-5]: GitPullRequestReviewState.ChangesRequested,\n\t[-10]: GitPullRequestReviewState.ChangesRequested,\n};\n\n/** provider-apis' reviewer mapping (`ve`), with the state its vote maps to: every reviewer, required or optional. */\nfunction toAzureProviderReview(reviewer: AzureUserWithVote): ProviderPullRequestReview {\n\treturn {\n\t\treviewer: {\n\t\t\tid: reviewer.id,\n\t\t\tname: reviewer.displayName ?? null,\n\t\t\tusername: (reviewer.uniqueName || reviewer.displayName) ?? null,\n\t\t\temail: null,\n\t\t\tavatarUrl: reviewer.imageUrl ?? null,\n\t\t\turl: null,\n\t\t},\n\t\tstate: toAzureProviderReviewState(reviewer),\n\t};\n}\n\nfunction toAzureProviderReviewState(reviewer: AzureUserWithVote): ProviderReviewState {\n\treturn azureVoteProviderReviewStates[reviewer.vote ?? 0] ?? GitPullRequestReviewState.ReviewRequested;\n}\n\n/**\n * A cheap read's pull request (the same `GET …/pullrequests/{id}` provider-apis sends), as the fields its full batch\n * row's etag reads: provider-apis' mapping (see the tables above), then `fromProviderPullRequest`'s. Azure DevOps\n * reports no update time, so provider-apis' `updatedDate` is the close time, else the creation time, which is why\n * the fields Azure changes without one are fingerprinted into the `revision`, from the values the full row ends up\n * with: provider-apis' title, description (`?? null`, which the row reads `?? undefined`) and target branch, and its\n * reviews, split into requests and completed reviews by `fromProviderPullRequest`'s own helpers.\n *\n * Throws where provider-apis' mapping throws, on a field it reads without a guard, so a pull request the full read\n * fails on falls through to it rather than matching an etag. A full row never carries a check rollup (provider-apis\n * maps none for Azure DevOps), so the `checks` include always reads `undefined`, as there.\n */\nexport function toAzurePullRequestEtagFields(\n\tpr: AzurePullRequest,\n\tetagIncludes: readonly PullRequestEtagInclude[],\n): PullRequestEtagFields {\n\tif (\n\t\tpr.pullRequestId == null ||\n\t\tpr.createdBy == null ||\n\t\tpr.repository?.project == null ||\n\t\tpr.lastMergeSourceCommit == null ||\n\t\tpr.lastMergeTargetCommit == null ||\n\t\t!Array.isArray(pr.reviewers) ||\n\t\tpr.reviewers.some(r => r == null) ||\n\t\ttypeof pr.url !== 'string' ||\n\t\ttypeof pr.sourceRefName !== 'string' ||\n\t\ttypeof pr.targetRefName !== 'string'\n\t) {\n\t\tthrow new Error(`Azure DevOps returned pull request ${String(pr.pullRequestId)} without a field it maps`);\n\t}\n\n\tconst reviews = pr.reviewers.map(toAzureProviderReview);\n\tconst fields: PullRequestEtagFields = {\n\t\t// `notSet` passes `undefined` here, as its full row does.\n\t\tstate: fromProviderPullRequestState(azurePullRequestProviderStates[pr.status] as ProviderPullRequestState),\n\t\tisDraft: pr.isDraft,\n\t\tupdatedDate: new Date(pr.closedDate || pr.creationDate),\n\t\theadSha: pr.lastMergeSourceCommit.commitId ?? '',\n\t\trevision: pullRequestRevision({\n\t\t\ttitle: pr.title,\n\t\t\tbody: pr.description ?? undefined,\n\t\t\trefs: { base: { branch: normalizeAzureBranchName(pr.targetRefName) } },\n\t\t\treviewRequests: toReviewRequests(reviews),\n\t\t\tlatestReviews: toCompletedReviews(reviews),\n\t\t}),\n\t};\n\n\tif (etagIncludes.includes('mergeable')) {\n\t\tconst mergeable =\n\t\t\t(pr.mergeStatus != null ? azureMergeStatusProviderStates[pr.mergeStatus] : undefined) ??\n\t\t\tGitPullRequestMergeableState.Unknown;\n\t\tfields.mergeableState = fromProviderPullRequestMergeableState[mergeable];\n\t}\n\n\tif (etagIncludes.includes('reviewDecision')) {\n\t\t// Only the required reviewers decide.\n\t\tconst decision = decideProviderReviewDecision(\n\t\t\tpr.reviewers.filter(r => r.isRequired).map(toAzureProviderReviewState),\n\t\t);\n\t\tfields.reviewDecision = decision ? fromPullRequestReviewDecision[decision] : undefined;\n\t}\n\n\treturn fields;\n}\n"],"mappings":";;;;;;;;AAkCA,MAAa,qBAAqB;;;;;;AA4ClC,SAAgB,wBACf,UACiC;CACjC,QAAQ,UAAR;EACC,KAAK,WACJ,OAAO;EACR,KAAK;EACL,KAAK,YACJ,OAAO;EACR,KAAK,cACJ,OAAO;EACR,KAAK,YACJ,OAAO;EACR,SACC;CACF;AACD;AAEA,SAAgB,mCAAmC,UAA2D;CAC7G,OAAO,6BAA6B,wBAAwB,QAAQ,CAAC;AACtE;AAEA,SAAgB,mCACf,UACA,YACsC;CACtC,IAAI,wBAAwB,QAAQ,KAAK,MACxC,OAAO,mCAAmC,QAAQ,IAAI,WAAW;CAGlE,OAAO,cAAc,OAAO,WAAW,KAAA;AACxC;AAiGA,SAAgB,8BAA8B,QAAyD;CACtG,QAAQ,QAAR;EACC,KAAK,aACJ,OAAO;EACR,KAAK,aACJ,OAAO;EAGR,SACC,OAAO;CACT;AACD;AACA,SAAgB,+BAA+B,QAAyC;CACvF,OAAO,8BAA8B,MAAM,MAAM;AAClD;AA4MA,SAAgB,oBAAoB,KAAkB;CACrD,OAAO,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC;AAChC;AACA,SAAgB,OAAO,QAAyB;CAC/C,OAAO,OAAO,YAAY,CAAC,CAAC,SAAS,kBAAkB;AACxD;;;;;AAMA,SAAgB,uBAAuB,OAAuB;CAC7D,IAAI,UAAU,OAAO,UAAU,MAAM,MAAM,IAAI,MAAM,+BAA+B,MAAM,GAAG;CAE7F,OAAO,mBAAmB,KAAK;AAChC;;;;;AAMA,SAAS,yBAAyB,SAAiB,OAAe,aAAqB,UAA0B;CAChH,MAAM,WAAW,GAAG,mBAAmB,KAAK,EAAE,GAAG,mBAAmB,WAAW,EAAE,QAAQ,mBAAmB,QAAQ;CACpH,OAAO,GAAG,QAAQ,QAAQ,QAAQ,EAAE,EAAE,GAAG;AAC1C;AAEA,SAAgB,0BAA0B,IAAsB,SAAiB,OAAuB;CAEvG,OAAO,GADS,yBAAyB,SAAS,OAAO,GAAG,WAAW,QAAQ,MAAM,GAAG,WAAW,IACnF,EAAE,eAAe,GAAG;AACrC;;;;;;AAOA,SAAS,6BAA6B,KAAU,UAAe,eAAgC;CAC9F,IAAI,IAAI,aAAa,SAAS,UAAU,OAAO;CAE/C,MAAM,QAAQ,cAAc,YAAY;CACxC,IAAI,SAAS,aAAa,mBAAmB,CAAC,OAAO,SAAS,QAAQ,GAAG,OAAO;CAEhF,IAAI,IAAI,aAAa,iBAAiB,OAAO,oBAAoB,GAAG,CAAC,CAAC,YAAY,MAAM;CAExF,OAAO,IAAI,aAAa,GAAG,QAAQ;AACpC;;;;;AAMA,SAAS,uBAAuB,KAAU,UAAe,eAAgC;CAExF,MAAM,SAAS,GADF,SAAS,SAAS,QAAQ,QAAQ,EAC1B,EAAE,GAAG,mBAAmB,aAAa,EAAE,GAAG,YAAY;CAC3E,OAAO,GAAG,IAAI,SAAS,GAAG,YAAY,CAAC,CAAC,WAAW,MAAM;AAC1D;;;;;;AAOA,SAAgB,2BACf,OACA,aACA,eACqB;CACrB,IAAI,SAAS,MAAM,OAAO,KAAA;CAE1B,IAAI;CACJ,IAAI;CACJ,IAAI;EACH,WAAW,IAAI,IAAI,WAAW;EAC9B,MAAM,IAAI,IAAI,KAAK;CACpB,QAAQ;EACP;CACD;CAEA,IAAI,SAAS,aAAa,YAAY,SAAS,aAAa,SAAS,OAAO,KAAA;CAC5E,IAAI,CAAC,eAAe,OAAO,KAAA;CAE3B,IAAI,IAAI,WAAW,SAAS,QACvB;MAAA,CAAC,uBAAuB,KAAK,UAAU,aAAa,GAAG,OAAO,KAAA;CAAA,OAC5D,IAAI,CAAC,6BAA6B,KAAK,UAAU,aAAa,GACpE;CAGD,IAAI,WAAW;CACf,IAAI,WAAW;CACf,IAAI,SAAS;CACb,IAAI,OAAO;CACX,OAAO,IAAI,SAAS;AACrB;;;;;;;;;;;;;;;;AAiBA,SAAS,2BACR,YACA,SACA,OACqB;CACrB,MAAM,YAAY,2BAA2B,WAAW,WAAW,SAAS,KAAK;CACjF,IAAI,aAAa,MAAM,OAAO,KAAA;CAG9B,MAAM,WAAW,IAAI,IAAI,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,CAAC,QAAO,MAAK,EAAE,SAAS,CAAC;CAChF,MAAM,OAAO,SAAS,IAAI;CAC1B,IAAI,QAAQ,QAAQ,SAAS,IAAI,MAAM,QAAQ,OAAO,KAAA;CAEtD,IAAI;CACJ,IAAI;EACH,UAAU,mBAAmB,IAAI;CAClC,QAAQ;EACP;CACD;CAIA,OAAO,QAAQ,YAAY,MAAM,WAAW,KAAK,YAAY,IAAI,YAAY,KAAA;AAC9E;AAEA,SAAgB,gDACf,aAC4B;CAC5B,QAAQ,aAAR;EACC,KAAK,aACJ,OAAA;EACD,KAAK,WACJ,OAAA;EACD,KAAK,oBACJ,OAAA;EACD,KAAK,aACJ,OAAA;EAGD,SACC,OAAA;CACF;AACD;AAEA,SAAgB,sCAAsC,MAAoD;CACzG,QAAQ,MAAR;EACC,KAAK;EACL,KAAK,GACJ,OAAA;EACD,KAAK,GACJ,OAAA;EACD,KAAK;EACL,KAAK,KACJ,OAAA;EACD,SACC,OAAA;CACF;AACD;AAEA,SAAgB,gCAAgC,UAAkD;CACjG,OAAO;EACN,aAAa,KAAA;EACb,UAAU;GACT,WAAW,SAAS;GACpB,IAAI,SAAS;GACb,MAAM,SAAS;GACf,UAAU,SAAS;GACnB,KAAK,SAAS;EACf;EACA,OAAO,sCAAsC,SAAS,QAAQ,CAAC;CAChE;AACD;AAEA,SAAgB,kCACf,OACwC;CACxC,MAAM,eAAe,MAAM,KAAI,SAAQ,sCAAsC,IAAI,CAAC;CAClF,IAAI,aAAa,SAAA,mBAAgD,GAChE,OAAA;CAGD,IAAI,aAAa,SAAA,kBAA+C,GAC/D,OAAA;CAGD,IAAI,aAAa,SAAA,UAAwC,GACxD,OAAA;AAIF;AAEA,SAAgB,qCAAqC,UAAwC;CAC5F,OAAO;EACN,WAAW,SAAS;EACpB,IAAI,SAAS;EACb,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,KAAK,SAAS;CACf;AACD;AAEA,SAAgB,yBAAyB,YAA4B;CACpE,OAAO,WAAW,WAAW,aAAa,IAAI,WAAW,QAAQ,eAAe,EAAE,IAAI;AACvF;AAIA,SAAS,sBAAsB,MAAiB,OAAiE;CAChH,OAAO;EACN,WAAW,KAAK;EAChB,IAAI,KAAK;EACT,MAAM,KAAK;EACX,UAAU,KAAK;EACf,KAAK,KAAK;CACX;AACD;;;;;AAoBA,SAAgB,qBACf,IACA,UACA,OACA,SACA,oBACA,YACc;CACd,MAAM,oBAAoB,yBAAyB,SAAS,OAAO,GAAG,WAAW,QAAQ,MAAM,GAAG,WAAW,IAAI;CACjH,MAAM,iBAAiB,2BAA2B,GAAG,YAAY,SAAS,KAAK;CAE/E,MAAM,iBAAiB,GAAG,YAAY;CACtC,MAAM,oBAAoB,kBAAkB,OAAO,oBAAoB,oBAAoB;CAC3F,MAAM,iBAAiB,kBAAkB,OAAO,iBAAiB,oBAAoB;CAErF,OAAO,IAAI,YACV,UACA,sBAAsB,GAAG,WAAW,aAAa,GACjD,GAAG,cAAc,SAAS,GAC1B,GAAG,cAAc,SAAS,GAC1B,GAAG,OACH,0BAA0B,IAAI,SAAS,KAAK,GAC5C;EACQ;EACP,MAAM,GAAG,WAAW;EACpB,IAAI,GAAG,WAAW;CACnB,GACA,8BAA8B,GAAG,MAAM,GACvC,IAAI,KAAK,GAAG,YAAY,GACxB,IAAI,KAAK,GAAG,cAAc,GAAG,YAAY,GACzC,GAAG,aAAa,IAAI,KAAK,GAAG,UAAU,IAAI,KAAA,GAC1C,GAAG,cAAc,GAAG,WAAW,cAAc,IAAI,KAAK,GAAG,UAAU,IAAI,KAAA,GAEvE,gDAAgD,GAAG,eAAe,QAAQ,GAC1E,KAAA,GACA;EACC,MAAM;GACL,QAAQ,GAAG,gBAAgB,yBAAyB,GAAG,aAAa,IAAI;GACxE,KAAK,GAAG,uBAAuB,YAAY;GAC3C,MAAM,GAAG,WAAW;GACb;GACP,QAAQ,GAAG,iBAAiB;GAC5B,KAAK;GACL,YAAY;EACb;EACA,MAAM;GACL,QAAQ,GAAG,gBAAgB,yBAAyB,GAAG,aAAa,IAAI;GACxE,KAAK,GAAG,uBAAuB,YAAY;GAC3C,MAAM,gBAAgB,QAAQ,GAAG,WAAW;GACrC;GACP,QAAQ,GAAG,iBAAiB;GAC5B,KAAK;GACL,YAAY;EACb;EACA,mBAAmB,GAAG,cAAc;CACrC,GACA,GAAG,SACH,KAAA,GACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,kCAAkC,GAAG,WAAW,QAAO,MAAK,EAAE,UAAU,CAAC,CAAC,KAAI,MAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,GACrG,GAAG,UAAU,QAAO,MAAK,EAAE,QAAQ,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,KAAI,MAAK,gCAAgC,CAAC,CAAC,GACpG,GAAG,UAAU,QAAO,MAAK,EAAE,QAAQ,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,KAAI,MAAK,gCAAgC,CAAC,CAAC,GACpG,GAAG,UAAU,KAAI,MAAK,qCAAqC,CAAC,CAAC,GAC7D,KAAA,GACA;EACC,IAAI,GAAG,YAAY,SAAS;EAC5B,MAAM,GAAG,WAAW,QAAQ;EAC5B,YAAY;EACZ,cAAc;CACf,GACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,UACD;AACD;AAEA,SAAgB,kBACf,UACA,UACA,SACA,eACA,YACA,YACQ;CACR,MAAM,QAAQ,SAAS,OAAO;CAC9B,MAAM,cAAc,4BAA4B,SAAS,QAAQ,UAAU;CAC3E,MAAM,aAAa,uBAAuB,SAAS,OAAO,mCAAmC,KAAK,KAAA;CAClG,MAAM,kBAAkB,mCAAmC,eAAe,UAAU;CACpF,OAAO,IAAIA,QACV,UACA,SAAS,GAAG,SAAS,GACrB,SAAS,GAAG,SAAS,GACrB,SAAS,OAAO,iBAChB,SAAS,OAAO,KAAK,MACrB,IAAI,KAAK,SAAS,OAAO,qBAAqB,GAC9C,IAAI,KAAK,SAAS,OAAO,qBAAqB,GAC9C,oBAAoB,UACpB,iBACA,sBAAsB,SAAS,OAAO,qBAAqB,OAAO,GAClE,SAAS,OAAO,wBAAwB,OACrC,CAAC,sBAAsB,SAAS,OAAO,sBAAsB,OAAO,CAAC,IACrE,CAAC,GACJ,KAAA,GACA,YACA,KAAA,GACA,SAAS,OAAO,wBAChB,KAAA,GACA,aAAa,OACb,SACA,KAAA,GACA,SAAS,OAAO,wBAChB,OAAO,UAAU,YAAY,QAC1B;EAAE,MAAM;EAAO,UAAU,wBAAwB,aAAa;CAAE,IAChE,KAAA,GACH,KAAA,GACA,qBAAqB,SAAS,OAAO,uBAAuB,GAC5D,YACA,eAAe,OAAO,iBAAiB,YAAY,KAAK,IAAI,KAAA,CAC7D;AACD;;;;;;;;;;AAWA,SAAS,qBAAqB,MAAwD;CACrF,IAAI,CAAC,MAAM,OAAO,KAAA;CAElB,MAAM,WAAW,KAAK,MAAM,IAAI;CAChC,IAAI,SAAS,SAAS,GAAG,OAAO,KAAA;CAEhC,MAAM,OAAO,SAAS,GAAG,EAAE,CAAC,EAAE,KAAK;CACnC,OAAO,OAAO,CAAC;EAAE,IAAI;EAAY;CAAK,CAAC,IAAI,KAAA;AAC5C;;;;;;;;;;;;AAgCA,SAAgB,iCACf,UACA,WACA,SAC4B;CAC5B,MAAM,SAAS,SAAS;CACxB,MAAM,KAAK,SAAS;CACpB,MAAM,QAAQ,SAAS;CACvB,MAAM,cAAc,uBAAuB,SAAS,qBAAqB;CACzE,IACC,UAAU,QACV,OAAO,OAAO,YACd,CAAC,OAAO,cAAc,EAAE,KACxB,MAAM,KACN,OAAO,UAAU,YACjB,CAAC,MAAM,KAAK,KACZ,eAAe,MAEf;CAGD,MAAM,WAAW,0BAA0B,OAAO,oBAAoB;CACtE,MAAM,eAAe,OAAO;CAC5B,MAAM,MAAM,SAAS,QAAQ,MAAM;CACnC,MAAM,OAAO,OAAO;CACpB,MAAM,QAAQ,OAAO;CACrB,MAAM,OAAO,OAAO;CACpB,OAAO;EACN,IAAI,GAAG,SAAS;EAChB,QAAQ,GAAG,SAAS;EACb;EACP,cAAc,OAAO,iBAAiB,YAAY,OAAO,SAAS,YAAY,IAAI,eAAe;EACjG,QAAQ,0BAA0B,OAAO,mBAAmB;EAC5D,YAAY,uBAAuB,OAAO,mCAAmC;EAChE;EACb,aAAa,uBAAuB,OAAO,qBAAqB;EAChE,KAAK,OAAO,QAAQ,WAAW,MAAM;EACrC,WAAW,YAAY,OAAO,CAAC,QAAQ,IAAI,CAAC;EAC5C,GAAG,iCAAiC,MAAM;EAC1C,OAAO,OAAO,UAAU,YAAY,QAAQ;GAAE,MAAM;GAAO,OAAO;EAAK,IAAI;EAC3E,MAAM,OAAO,SAAS,WAAW,OAAO;EACxC,WAAW,yBAAyB,OAAO,uBAAuB;EAClE,YAAY;EACZ,SAAS;GAAa;GAAW,MAAM;GAAS,YAAY;GAAM,KAAK;GAAM,IAAI;EAAK;EAEtF,aAAa;EACb,SAAS,OAAO,SAAS,WAAW,KAAK,MAAM,GAAG,IAAI,CAAC,EAAA,CAAG,KAAI,SAAQ;GACrE,OAAO;GACP,aAAa;GACb,IAAI;GACJ,MAAM,IAAI,KAAK;EAChB,EAAE;CACH;AACD;;;;;;AAOA,SAAgB,iCACf,QACA,YAC0D;CAC1D,MAAM,cAAc,4BAA4B,QAAQ,UAAU;CAClE,OAAO;EACN,aAAa,aAAa,SAAS;EACnC,GAAI,eAAe,OAAO,EAAE,kBAAkB,YAAY,MAAM,IAAI,CAAC;CACtE;AACD;;AAGA,SAAS,0BAA0B,OAAwC;CAC1E,MAAM,WAAW;CAIjB,IAAI,OAAO,UAAU,OAAO,YAAY,CAAC,SAAS,GAAG,KAAK,GAAG,OAAO;CAEpE,MAAM,YAAY,SAAS,QAAQ,QAAQ;CAC3C,OAAO;EACN,WAAW,OAAO,cAAc,WAAW,YAAY;EACvD,OAAO;EACP,IAAI,SAAS;EACb,MAAM,OAAO,SAAS,eAAe,WAAW,SAAS,aAAa;EACtE,UAAU,OAAO,SAAS,gBAAgB,WAAW,SAAS,cAAc;EAC5E,KAAK;CACN;AACD;;AAGA,SAAgB,uBAAuB,OAA6B;CACnE,IAAI,OAAO,UAAU,UAAU,OAAO;CAEtC,MAAM,QAAQ,mEAAmE,KAAK,KAAK;CAC3F,IAAI,SAAS,MAAM,OAAO;CAE1B,MAAM,CAAC,MAAM,OAAO,KAAK,MAAM,QAAQ,UAAU,MAAM,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM;CAE1E,MAAM,cAAc;EAAC;EADF,OAAO,MAAM,MAAM,OAAO,QAAQ,KAAK,OAAO,QAAQ,KACnC,KAAK;EAAI;EAAI;EAAI;EAAI;EAAI;EAAI;EAAI;EAAI;EAAI;EAAI;CAAE;CACrF,IAAI,QAAQ,KAAK,QAAQ,MAAM,MAAM,KAAK,MAAM,YAAY,QAAQ,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,IAC9G,OAAO;CAGR,MAAM,OAAO,IAAI,KAAK,KAAK;CAC3B,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;AAC9C;;AAGA,SAAS,yBAAyB,MAA2C;CAC5E,MAAM,CAAC,aAAa,qBAAqB,OAAO,SAAS,WAAW,OAAO,KAAA,CAAS,KAAK,CAAC;CAC1F,OAAO,aAAa,OAAO;EAAE,MAAM,UAAU;EAAI,MAAM,UAAU;CAAK,IAAI,KAAA;AAC3E;;;;;AAMA,MAAa,iCAAiC;;;;;;AAO9C,MAAa,8BAAiD;CAC7D;CACA;CACA;CACA;CACA;AACD;;;;;;;;AASA,SAAgB,0BACf,UACA,SACA,UACkB;CAClB,MAAM,SAAS,SAAS;CACxB,MAAM,cAAc,SAAS;CAC7B,IAAI,OAAO,gBAAgB,YAAY,YAAY,YAAY,MAAM,QAAQ,YAAY,GACxF,MAAM,IAAI,MACT,0BAA0B,OAAO,SAAS,EAAE,EAAE,kBAAkB,YAAY,UAAU,QAAQ,EAC/F;CAGD,MAAM,cAAc,uBAAuB,SAAS,qBAAqB;CACzE,IAAI,eAAe,MAClB,MAAM,IAAI,MAAM,mCAAmC,OAAO,SAAS,EAAE,EAAE,uBAAuB;CAW/F,OAAO;EACN,OARA,YAAY,OACT,6BAA6B,QAAQ,IACpC,WACA,WACD,uBAAuB,SAAS,mCAAmC,KAAK,OACvE,WACA,KAAA;EAGS;EACb,iBAAA;CACD;AACD;;AAWA,MAAM,iCAAoF;CACzF,QAAQC,sBAAoB;CAC5B,WAAWA,sBAAoB;CAC/B,WAAWA,sBAAoB;AAChC;;AAGA,MAAM,iCAAkF;CACvF,WAAWC,+BAA6B;CACxC,SAASA,+BAA6B;CACtC,kBAAkBA,+BAA6B;CAC/C,WAAWA,+BAA6B;AACzC;;AAGA,MAAM,gCAA8E;CACnF,IAAIC,4BAA0B;CAC9B,GAAGA,4BAA0B;CAC7B,GAAGA,4BAA0B;EAC5B,KAAKA,4BAA0B;EAC/B,MAAMA,4BAA0B;AAClC;;AAGA,SAAS,sBAAsB,UAAwD;CACtF,OAAO;EACN,UAAU;GACT,IAAI,SAAS;GACb,MAAM,SAAS,eAAe;GAC9B,WAAW,SAAS,cAAc,SAAS,gBAAgB;GAC3D,OAAO;GACP,WAAW,SAAS,YAAY;GAChC,KAAK;EACN;EACA,OAAO,2BAA2B,QAAQ;CAC3C;AACD;AAEA,SAAS,2BAA2B,UAAkD;CACrF,OAAO,8BAA8B,SAAS,QAAQ,MAAMA,4BAA0B;AACvF;;;;;;;;;;;;;AAcA,SAAgB,6BACf,IACA,cACwB;CACxB,IACC,GAAG,iBAAiB,QACpB,GAAG,aAAa,QAChB,GAAG,YAAY,WAAW,QAC1B,GAAG,yBAAyB,QAC5B,GAAG,yBAAyB,QAC5B,CAAC,MAAM,QAAQ,GAAG,SAAS,KAC3B,GAAG,UAAU,MAAK,MAAK,KAAK,IAAI,KAChC,OAAO,GAAG,QAAQ,YAClB,OAAO,GAAG,kBAAkB,YAC5B,OAAO,GAAG,kBAAkB,UAE5B,MAAM,IAAI,MAAM,sCAAsC,OAAO,GAAG,aAAa,EAAE,yBAAyB;CAGzG,MAAM,UAAU,GAAG,UAAU,IAAI,qBAAqB;CACtD,MAAM,SAAgC;EAErC,OAAO,6BAA6B,+BAA+B,GAAG,OAAmC;EACzG,SAAS,GAAG;EACZ,aAAa,IAAI,KAAK,GAAG,cAAc,GAAG,YAAY;EACtD,SAAS,GAAG,sBAAsB,YAAY;EAC9C,UAAU,oBAAoB;GAC7B,OAAO,GAAG;GACV,MAAM,GAAG,eAAe,KAAA;GACxB,MAAM,EAAE,MAAM,EAAE,QAAQ,yBAAyB,GAAG,aAAa,EAAE,EAAE;GACrE,gBAAgB,iBAAiB,OAAO;GACxC,eAAe,mBAAmB,OAAO;EAC1C,CAAC;CACF;CAEA,IAAI,aAAa,SAAS,WAAW,GAAG;EACvC,MAAM,aACJ,GAAG,eAAe,OAAO,+BAA+B,GAAG,eAAe,KAAA,MAC3ED,+BAA6B;EAC9B,OAAO,iBAAiB,sCAAsC;CAC/D;CAEA,IAAI,aAAa,SAAS,gBAAgB,GAAG;EAE5C,MAAM,WAAW,6BAChB,GAAG,UAAU,QAAO,MAAK,EAAE,UAAU,CAAC,CAAC,IAAI,0BAA0B,CACtE;EACA,OAAO,iBAAiB,WAAW,8BAA8B,YAAY,KAAA;CAC9E;CAEA,OAAO;AACR"}
1
+ {"version":3,"file":"models.js","names":["Issue","GitPullRequestState","GitPullRequestMergeableState","GitPullRequestReviewState"],"sources":["../../../../../../plus/integrations/src/providers/azure/models.ts"],"sourcesContent":["import type { AzureWorkItemHtmlField } from '@gitkraken/provider-apis';\nimport { getAzureWorkItemDescription } from '@gitkraken/provider-apis';\nimport type { IssueIteration, IssueMember, IssueProjection, IssueProviderState } from '@gitlens/git/models/issue.js';\nimport { Issue } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequestState } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequestMember, PullRequestProjection, 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 { isTerminalIssueStateCategory } from '@gitlens/git/utils/issue.utils.js';\nimport type { IssueEtagFields, PullRequestEtagFields, PullRequestEtagInclude } from '../../models/integration.js';\nimport { azureWorkItemDescriptionRule, pullRequestRevision } from '../../models/integration.js';\nimport type { ProviderAccount, ProviderIssue } from '../models.js';\nimport {\n\tfromProviderPullRequestMergeableState,\n\tfromProviderPullRequestState,\n\tGitPullRequestMergeableState,\n\tGitPullRequestReviewState,\n\tGitPullRequestState,\n\ttoIssueBodyField,\n} from '../models.js';\nimport type { ProviderPullRequestReview } from '../pullRequestReviews.js';\nimport {\n\tdecideProviderReviewDecision,\n\tfromPullRequestReviewDecision,\n\ttoCompletedReviews,\n\ttoReviewRequests,\n} from '../pullRequestReviews.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\n/**\n * The workflow category of an Azure DevOps state category, as a work item type's states endpoint reports it:\n * `Removed` is abandoned work, so `CANCELED`, kept apart from the `DONE` of `Completed` and `Resolved`.\n * `undefined` for a category Azure doesn't document, which then neither opens nor closes the work item.\n */\nexport function azureIssueStateCategory(\n\tcategory: AzureWorkItemStateCategory | undefined,\n): IssueProviderState['category'] {\n\tswitch (category) {\n\t\tcase 'Removed':\n\t\t\treturn 'CANCELED';\n\t\tcase 'Completed':\n\t\tcase 'Resolved':\n\t\t\treturn 'DONE';\n\t\tcase 'InProgress':\n\t\t\treturn 'IN_PROGRESS';\n\t\tcase 'Proposed':\n\t\t\treturn 'TO_DO';\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function isClosedAzureWorkItemStateCategory(category: AzureWorkItemStateCategory | undefined): boolean {\n\treturn isTerminalIssueStateCategory(azureIssueStateCategory(category));\n}\n\nexport function azureWorkItemsStateCategoryToState(\n\tcategory: AzureWorkItemStateCategory | undefined,\n\tclosedDate?: Date,\n): IssueOrPullRequestState | undefined {\n\tif (azureIssueStateCategory(category) != null) {\n\t\treturn isClosedAzureWorkItemStateCategory(category) ? 'closed' : 'opened';\n\t}\n\n\treturn closedDate != null ? 'closed' : undefined;\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 | string;\n\t\t'System.CreatedDate': string;\n\t\t'System.CreatedBy': AzureUser | string;\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.toLowerCase().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\tprojection?: PullRequestProjection,\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},\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\t// The read selects the merge status, so an absent one is Azure's own \"not set\", as provider-apis maps it.\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\tundefined, // version\n\t\tundefined, // commitCount\n\t\tundefined, // stack\n\t\tundefined, // filesChanged\n\t\tundefined, // body\n\t\tundefined, // number\n\t\tundefined, // authoredByMe\n\t\tprojection,\n\t);\n}\n\nfunction fromAzureWorkItemUserToMember(user: AzureUser | string | undefined): IssueMember | undefined {\n\tif (user == null || (typeof user === 'string' && !user.trim())) return undefined;\n\n\treturn typeof user === 'string' ? { id: user, name: user, username: user } : fromAzureUserToMember(user, 'issue');\n}\n\nexport function fromAzureWorkItem(\n\tworkItem: WorkItem,\n\tprovider: Provider,\n\tproject: AzureProjectDescriptor,\n\tstateCategory?: AzureWorkItemStateCategory,\n\tprojection?: IssueProjection,\n\thtmlFields?: readonly AzureWorkItemHtmlField[],\n): Issue {\n\tconst state = workItem.fields['System.State'];\n\tconst description = getAzureWorkItemDescription(workItem.fields, htmlFields);\n\tconst closedDate = parseAzureWorkItemDate(workItem.fields['Microsoft.VSTS.Common.ClosedDate']) ?? undefined;\n\tconst normalizedState = azureWorkItemsStateCategoryToState(stateCategory, closedDate);\n\tconst assignee = fromAzureWorkItemUserToMember(workItem.fields['System.AssignedTo']);\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\tnormalizedState === 'closed',\n\t\tnormalizedState,\n\t\tfromAzureWorkItemUserToMember(workItem.fields['System.CreatedBy']),\n\t\tassignee != null ? [assignee] : [],\n\t\tundefined,\n\t\tclosedDate,\n\t\tundefined,\n\t\tworkItem.fields['System.CommentCount'],\n\t\tundefined,\n\t\tdescription?.value,\n\t\tproject,\n\t\tundefined,\n\t\tworkItem.fields['System.WorkItemType'],\n\t\ttypeof state === 'string' && state\n\t\t\t? { name: state, category: azureIssueStateCategory(stateCategory) }\n\t\t\t: undefined,\n\t\tundefined,\n\t\ttoWorkItemIterations(workItem.fields['System.IterationPath']),\n\t\tprojection,\n\t\tdescription != null ? toIssueBodyField(description.field) : undefined,\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 * A cheap read's work item with the workflow category its state classifies to (see\n * {@link toAzureWorkItemEtagFields}). Beside the work item rather than on it, so the wire type stays what Azure sends.\n */\nexport interface AzureWorkItemEtagRead {\n\treadonly workItem: AzureWorkItemResponse;\n\treadonly category: IssueProviderState['category'];\n}\n\n/**\n * Converts a work item as provider-apis' `getIssuesForAzureProject` converts each row it reads, so a work item read\n * by id reaches `toIssueShape` identical to the list read's row for it. provider-apis exports neither that converter\n * nor a single work item read, so this mirrors it field for field; the batch issue tests compare the two, so a\n * 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. Like\n * the SDK's row, the state carries no workflow category: `ProvidersApi` classifies both reads' rows the same way\n * afterwards, from the work item type's states.\n */\nexport function fromAzureWorkItemToProviderIssue(\n\tworkItem: AzureWorkItemResponse,\n\tnamespace: string,\n\tproject: string,\n\thtmlFields?: readonly AzureWorkItemHtmlField[],\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\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\t...toAzureWorkItemDescriptionFields(fields, htmlFields),\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\t// provider-apis fills 0, but work items have no reactions.\n\t\tupvoteCount: null,\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\nexport function toAzureWorkItemDescriptionFields(\n\tfields: Readonly<Record<string, unknown>> | null | undefined,\n\thtmlFields?: readonly AzureWorkItemHtmlField[],\n): Pick<ProviderIssue, 'description' | 'descriptionField'> {\n\tconst description = getAzureWorkItemDescription(fields, htmlFields);\n\treturn {\n\t\tdescription: description?.value ?? null,\n\t\t...(description != null ? { descriptionField: description.field } : {}),\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\tif (typeof value === 'string') {\n\t\tif (!value.trim()) return null;\n\n\t\treturn { id: value, name: value, username: value, email: null, avatarUrl: null, url: null };\n\t}\n\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`. */\nexport function 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\n/**\n * The most ids one cheap work item read asks for: the most Azure DevOps' work item list (`GET …/_apis/wit/workitems`)\n * takes.\n */\nexport const azureWorkItemsEtagFieldsMaxIds = 200;\n\n/**\n * The fields the cheap work item read selects: only the ones a full row's etag reads, and the project the full read\n * checks the work item against. The state and type let the cheap read resolve the same workflow category as the full\n * read, from the same work item type states.\n */\nexport const azureWorkItemEtagFieldNames: readonly string[] = [\n\t'System.TeamProject',\n\t'System.State',\n\t'System.WorkItemType',\n\t'System.ChangedDate',\n\t'Microsoft.VSTS.Common.ClosedDate',\n];\n\n/**\n * A cheap read's work item, as the fields its full batch row's etag reads: `toIssueShape` closes the row the full\n * read builds (see {@link fromAzureWorkItemToProviderIssue}) from its known workflow category, falling back to a\n * closed date only when the category is unavailable. A historical date must not close a reopened work item.\n * Throws where the full read fails the work item: on one in another project than `project` (compared as Azure\n * compares names), and on a missing or unparseable change date, so the target falls through to the full read.\n */\nexport function toAzureWorkItemEtagFields(\n\tworkItem: AzureWorkItemResponse,\n\tproject: string,\n\tcategory: IssueProviderState['category'],\n): IssueEtagFields {\n\tconst fields = workItem.fields;\n\tconst teamProject = fields?.['System.TeamProject'];\n\tif (typeof teamProject !== 'string' || !teamProject.trim()) {\n\t\tthrow new Error(`Azure DevOps work item ${String(workItem.id)} has no project`);\n\t}\n\n\tif (teamProject.toLowerCase() !== project.toLowerCase()) {\n\t\tthrow new Error(\n\t\t\t`Azure DevOps work item ${String(workItem.id)} is in project '${teamProject}', not '${project}'`,\n\t\t);\n\t}\n\n\tconst updatedDate = parseAzureWorkItemDate(fields?.['System.ChangedDate']);\n\tif (updatedDate == null) {\n\t\tthrow new Error(`Azure DevOps returned work item ${String(workItem.id)} without a change date`);\n\t}\n\n\tconst state =\n\t\tcategory != null\n\t\t\t? isTerminalIssueStateCategory(category)\n\t\t\t\t? 'closed'\n\t\t\t\t: 'opened'\n\t\t\t: parseAzureWorkItemDate(fields?.['Microsoft.VSTS.Common.ClosedDate']) != null\n\t\t\t\t? 'closed'\n\t\t\t\t: undefined;\n\treturn {\n\t\tstate: state,\n\t\tupdatedDate: updatedDate,\n\t\tdescriptionRule: azureWorkItemDescriptionRule,\n\t};\n}\n\ntype ProviderPullRequestState = Parameters<typeof fromProviderPullRequestState>[0];\ntype ProviderMergeableState = keyof typeof fromProviderPullRequestMergeableState;\ntype ProviderReviewState = keyof typeof fromPullRequestReviewDecision;\n\n// The tables below mirror provider-apis' (0.61.0) Azure DevOps pull request mapping behind `getPullRequestForRepo` (`Xe`\n// and its helpers in the bundle), which it doesn't export. A full batch row takes these values on through\n// `fromProviderPullRequest`, so the cheap check composes the same steps to compute the same etag.\n\n/** provider-apis' pull request `status` map (`bi`). It has no `notSet`, which therefore maps to `undefined`. */\nconst azurePullRequestProviderStates: Partial<Record<string, ProviderPullRequestState>> = {\n\tactive: GitPullRequestState.Open,\n\tcompleted: GitPullRequestState.Merged,\n\tabandoned: GitPullRequestState.Closed,\n};\n\n/** provider-apis' `mergeStatus` map (`Ti`); any other status, or none, is unknown. */\nconst azureMergeStatusProviderStates: Partial<Record<string, ProviderMergeableState>> = {\n\tconflicts: GitPullRequestMergeableState.Conflicts,\n\tfailure: GitPullRequestMergeableState.FailingChecks,\n\trejectedByPolicy: GitPullRequestMergeableState.Blocked,\n\tsucceeded: GitPullRequestMergeableState.Mergeable,\n};\n\n/** provider-apis' reviewer `vote` map (`Di`); any other vote reads as review requested. */\nconst azureVoteProviderReviewStates: Partial<Record<number, ProviderReviewState>> = {\n\t10: GitPullRequestReviewState.Approved,\n\t5: GitPullRequestReviewState.Approved,\n\t0: GitPullRequestReviewState.ReviewRequested,\n\t[-5]: GitPullRequestReviewState.ChangesRequested,\n\t[-10]: GitPullRequestReviewState.ChangesRequested,\n};\n\n/** provider-apis' reviewer mapping (`ve`), with the state its vote maps to: every reviewer, required or optional. */\nfunction toAzureProviderReview(reviewer: AzureUserWithVote): ProviderPullRequestReview {\n\treturn {\n\t\treviewer: {\n\t\t\tid: reviewer.id,\n\t\t\tname: reviewer.displayName ?? null,\n\t\t\tusername: (reviewer.uniqueName || reviewer.displayName) ?? null,\n\t\t\temail: null,\n\t\t\tavatarUrl: reviewer.imageUrl ?? null,\n\t\t\turl: null,\n\t\t},\n\t\tstate: toAzureProviderReviewState(reviewer),\n\t};\n}\n\nfunction toAzureProviderReviewState(reviewer: AzureUserWithVote): ProviderReviewState {\n\treturn azureVoteProviderReviewStates[reviewer.vote ?? 0] ?? GitPullRequestReviewState.ReviewRequested;\n}\n\n/**\n * A cheap read's pull request (the same `GET …/pullrequests/{id}` provider-apis sends), as the fields its full batch\n * row's etag reads: provider-apis' mapping (see the tables above), then `fromProviderPullRequest`'s. Azure DevOps\n * reports no update time, so provider-apis' `updatedDate` is the close time, else the creation time, which is why\n * the fields Azure changes without one are fingerprinted into the `revision`, from the values the full row ends up\n * with: provider-apis' title, description (`?? null`, which the row reads `?? undefined`) and target branch, and its\n * reviews, split into requests and completed reviews by `fromProviderPullRequest`'s own helpers.\n *\n * Throws where provider-apis' mapping throws, on a field it reads without a guard, so a pull request the full read\n * fails on falls through to it rather than matching an etag. A full row never carries a check rollup (provider-apis\n * maps none for Azure DevOps), so the `checks` include always reads `undefined`, as there.\n */\nexport function toAzurePullRequestEtagFields(\n\tpr: AzurePullRequest,\n\tetagIncludes: readonly PullRequestEtagInclude[],\n): PullRequestEtagFields {\n\tif (\n\t\tpr.pullRequestId == null ||\n\t\tpr.createdBy == null ||\n\t\tpr.repository?.project == null ||\n\t\tpr.lastMergeSourceCommit == null ||\n\t\tpr.lastMergeTargetCommit == null ||\n\t\t!Array.isArray(pr.reviewers) ||\n\t\tpr.reviewers.some(r => r == null) ||\n\t\ttypeof pr.url !== 'string' ||\n\t\ttypeof pr.sourceRefName !== 'string' ||\n\t\ttypeof pr.targetRefName !== 'string'\n\t) {\n\t\tthrow new Error(`Azure DevOps returned pull request ${String(pr.pullRequestId)} without a field it maps`);\n\t}\n\n\tconst reviews = pr.reviewers.map(toAzureProviderReview);\n\tconst fields: PullRequestEtagFields = {\n\t\t// `notSet` passes `undefined` here, as its full row does.\n\t\tstate: fromProviderPullRequestState(azurePullRequestProviderStates[pr.status] as ProviderPullRequestState),\n\t\tisDraft: pr.isDraft,\n\t\tupdatedDate: new Date(pr.closedDate || pr.creationDate),\n\t\theadSha: pr.lastMergeSourceCommit.commitId ?? '',\n\t\trevision: pullRequestRevision({\n\t\t\ttitle: pr.title,\n\t\t\tbody: pr.description ?? undefined,\n\t\t\trefs: { base: { branch: normalizeAzureBranchName(pr.targetRefName) } },\n\t\t\treviewRequests: toReviewRequests(reviews),\n\t\t\tlatestReviews: toCompletedReviews(reviews),\n\t\t}),\n\t};\n\n\tif (etagIncludes.includes('mergeable')) {\n\t\tconst mergeable =\n\t\t\t(pr.mergeStatus != null ? azureMergeStatusProviderStates[pr.mergeStatus] : undefined) ??\n\t\t\tGitPullRequestMergeableState.Unknown;\n\t\tfields.mergeableState = fromProviderPullRequestMergeableState[mergeable];\n\t}\n\n\tif (etagIncludes.includes('reviewDecision')) {\n\t\t// Only the required reviewers decide.\n\t\tconst decision = decideProviderReviewDecision(\n\t\t\tpr.reviewers.filter(r => r.isRequired).map(toAzureProviderReviewState),\n\t\t);\n\t\tfields.reviewDecision = decision ? fromPullRequestReviewDecision[decision] : undefined;\n\t}\n\n\treturn fields;\n}\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { AzureWorkItemStatesScope } from "./workItemStateCategories.js";
2
+ import { AzureWorkItemHtmlField } from "@gitkraken/provider-apis";
3
+ //#region ../plus/integrations/src/providers/azure/workItemDescriptions.d.ts
4
+ export declare class AzureWorkItemDescriptionsCache {
5
+ private readonly fields;
6
+ clear(): void;
7
+ getFields(fields: Readonly<Record<string, unknown>> | undefined, scope: Omit<AzureWorkItemStatesScope, 'project'>, read: (signal?: AbortSignal) => Promise<unknown>, cancellation?: AbortSignal): Promise<readonly AzureWorkItemHtmlField[]>;
8
+ }
9
+ //#endregion
10
+ //# sourceMappingURL=workItemDescriptions.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { isCancellationError } from "../../../../utils/cancellation.js";
2
+ import { PromiseCache } from "../../../../utils/promiseCache.js";
3
+ import { encodeAzurePathSegment } from "./models.js";
4
+ import { needsAzureWorkItemHtmlFields, toAzureWorkItemHtmlFields } from "@gitkraken/provider-apis";
5
+ //#region ../plus/integrations/src/providers/azure/workItemDescriptions.ts
6
+ var AzureWorkItemDescriptionsCache = class {
7
+ fields = new PromiseCache({
8
+ createTTL: 36e5,
9
+ capacity: 256
10
+ });
11
+ clear() {
12
+ this.fields.clear();
13
+ }
14
+ async getFields(fields, scope, read, cancellation) {
15
+ if (!needsAzureWorkItemHtmlFields(fields)) return [];
16
+ encodeAzurePathSegment(scope.namespace);
17
+ const key = JSON.stringify([
18
+ scope.baseUrl.replace(/\/$/, ""),
19
+ scope.namespace.toLowerCase(),
20
+ scope.token,
21
+ scope.isPAT
22
+ ]);
23
+ const result = await this.fields.getOrCreate(key, async (_cacheable, signal) => {
24
+ const value = (await read(signal))?.value;
25
+ if (!Array.isArray(value)) throw new Error("Azure DevOps returned no work item field catalog");
26
+ return {
27
+ status: "fulfilled",
28
+ value: toAzureWorkItemHtmlFields(value)
29
+ };
30
+ }, {
31
+ cancellation,
32
+ onError: (error) => isCancellationError(error) || this.fields.get(key) == null ? void 0 : {
33
+ value: {
34
+ status: "rejected",
35
+ reason: error
36
+ },
37
+ createTTL: 3e4,
38
+ suppress: true
39
+ }
40
+ });
41
+ if (result.status === "rejected") throw result.reason;
42
+ return result.value;
43
+ }
44
+ };
45
+ //#endregion
46
+ export { AzureWorkItemDescriptionsCache };
47
+
48
+ //# sourceMappingURL=workItemDescriptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workItemDescriptions.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemDescriptions.ts"],"sourcesContent":["import type { AzureWorkItemHtmlField } from '@gitkraken/provider-apis';\nimport { needsAzureWorkItemHtmlFields, toAzureWorkItemHtmlFields } from '@gitkraken/provider-apis';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport { encodeAzurePathSegment } from './models.js';\nimport type { AzureWorkItemStatesScope } from './workItemStateCategories.js';\n\nexport class AzureWorkItemDescriptionsCache {\n\tprivate readonly fields = new PromiseCache<string, PromiseSettledResult<AzureWorkItemHtmlField[]>>({\n\t\tcreateTTL: 60 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tclear(): void {\n\t\tthis.fields.clear();\n\t}\n\n\tasync getFields(\n\t\tfields: Readonly<Record<string, unknown>> | undefined,\n\t\tscope: Omit<AzureWorkItemStatesScope, 'project'>,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<readonly AzureWorkItemHtmlField[]> {\n\t\tif (!needsAzureWorkItemHtmlFields(fields)) return [];\n\n\t\tencodeAzurePathSegment(scope.namespace);\n\t\tconst key = JSON.stringify([\n\t\t\tscope.baseUrl.replace(/\\/$/, ''),\n\t\t\tscope.namespace.toLowerCase(),\n\t\t\tscope.token,\n\t\t\tscope.isPAT,\n\t\t]);\n\t\tconst result = await this.fields.getOrCreate(\n\t\t\tkey,\n\t\t\tasync (_cacheable, signal) => {\n\t\t\t\tconst body = await read(signal);\n\t\t\t\tconst value = (body as { value?: unknown } | null)?.value;\n\t\t\t\tif (!Array.isArray(value)) throw new Error('Azure DevOps returned no work item field catalog');\n\n\t\t\t\treturn { status: 'fulfilled', value: toAzureWorkItemHtmlFields(value) };\n\t\t\t},\n\t\t\t{\n\t\t\t\tcancellation: cancellation,\n\t\t\t\tonError: error =>\n\t\t\t\t\tisCancellationError(error) || this.fields.get(key) == null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: { value: { status: 'rejected', reason: error }, createTTL: 30 * 1000, suppress: true },\n\t\t\t},\n\t\t);\n\t\tif (result.status === 'rejected') throw result.reason;\n\n\t\treturn result.value;\n\t}\n}\n"],"mappings":";;;;;AAOA,IAAa,iCAAb,MAA4C;CAC3C,SAA0B,IAAI,aAAqE;EAClG,WAAW;EACX,UAAU;CACX,CAAC;CAED,QAAc;EACb,KAAK,OAAO,MAAM;CACnB;CAEA,MAAM,UACL,QACA,OACA,MACA,cAC6C;EAC7C,IAAI,CAAC,6BAA6B,MAAM,GAAG,OAAO,CAAC;EAEnD,uBAAuB,MAAM,SAAS;EACtC,MAAM,MAAM,KAAK,UAAU;GAC1B,MAAM,QAAQ,QAAQ,OAAO,EAAE;GAC/B,MAAM,UAAU,YAAY;GAC5B,MAAM;GACN,MAAM;EACP,CAAC;EACD,MAAM,SAAS,MAAM,KAAK,OAAO,YAChC,KACA,OAAO,YAAY,WAAW;GAE7B,MAAM,SAAS,MADI,KAAK,MAAM,EAAA,EACsB;GACpD,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,kDAAkD;GAE7F,OAAO;IAAE,QAAQ;IAAa,OAAO,0BAA0B,KAAK;GAAE;EACvE,GACA;GACe;GACd,UAAS,UACR,oBAAoB,KAAK,KAAK,KAAK,OAAO,IAAI,GAAG,KAAK,OACnD,KAAA,IACA;IAAE,OAAO;KAAE,QAAQ;KAAY,QAAQ;IAAM;IAAG,WAAW;IAAW,UAAU;GAAK;EAC1F,CACD;EACA,IAAI,OAAO,WAAW,YAAY,MAAM,OAAO;EAE/C,OAAO,OAAO;CACf;AACD"}
@@ -1,6 +1,14 @@
1
- import { AzureWorkItemHtmlField, AzureWorkItemTypeStates } from "@gitkraken/provider-apis";
1
+ import { AzureWorkItemStateCategory } from "./models.js";
2
+ import { AzureWorkItemTypeStates } from "@gitkraken/provider-apis";
2
3
  //#region ../plus/integrations/src/providers/azure/workItemStateCategories.d.ts
3
- type WorkItemTypeStates = NonNullable<AzureWorkItemTypeStates['types'][number]['states']>;
4
+ type WorkItemTypeStates = readonly {
5
+ name: string;
6
+ category?: string;
7
+ }[];
8
+ type WorkItemTypeStatesSnapshot = {
9
+ states: WorkItemTypeStates;
10
+ usesLegacyMappings: boolean;
11
+ };
4
12
  export type AzureWorkItemStatesScope = {
5
13
  baseUrl: string;
6
14
  namespace: string;
@@ -8,30 +16,23 @@ export type AzureWorkItemStatesScope = {
8
16
  token: string;
9
17
  isPAT: boolean;
10
18
  };
11
- /** Where an organization's (or a Server collection's) field catalog is read: fields are defined per organization. */
12
- export declare function getAzureWorkItemFieldsPath(namespace: string): string;
13
- export declare function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string): string;
14
- export declare function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string): string;
15
- export declare function getAzureWorkItemTypeStatesUrl(baseUrl: string, namespace: string, project: string, type: string): string;
19
+ export declare function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string, apiVersion?: string): string;
20
+ export declare function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string, apiVersion?: string): string;
21
+ export declare function getAzureWorkItemTypeStatesUrl(baseUrl: string, namespace: string, project: string, type: string, apiVersion?: string): string;
22
+ export declare function resolveAzureWorkItemStateCategory(snapshot: WorkItemTypeStatesSnapshot, state: string): AzureWorkItemStateCategory | undefined;
23
+ export declare function requireAzureWorkItemStateCategories(snapshot: WorkItemTypeStatesSnapshot): NonNullable<AzureWorkItemTypeStates['types'][number]['states']>;
16
24
  /**
17
- * Workflow metadata per project, work item type and token, kept for 5 minutes, and each organization's rich text
18
- * fields per token, kept for an hour since they change only when an administrator edits a process. A failed read is
19
- * kept for 30 seconds, so the pages and per-type reads of a listing fail fast with the same error instead of each
20
- * asking a failing server again; the first read after that retries.
25
+ * Workflow metadata per project, work item type and token, kept for 5 minutes. A failed read is kept for 30 seconds,
26
+ * so the pages and per-type reads of a listing fail fast with the same error instead of each asking a failing server
27
+ * again; the first read after that retries.
21
28
  */
22
29
  export declare class AzureWorkItemStatesCache {
23
30
  private readonly types;
24
31
  private readonly states;
25
- private readonly htmlFields;
32
+ private readonly mappings;
26
33
  clear(): void;
27
- /**
28
- * The rich text fields of the scope's organization that may stand in for a work item's description, in the order
29
- * provider-apis' `getAzureWorkItemDescription` tries them. The scope's project is ignored: fields are defined per
30
- * organization.
31
- */
32
- getHtmlFields(scope: Omit<AzureWorkItemStatesScope, 'project'>, read: (signal?: AbortSignal) => Promise<unknown>, cancellation?: AbortSignal): Promise<AzureWorkItemHtmlField[]>;
33
34
  getTypes(scope: AzureWorkItemStatesScope, read: (signal?: AbortSignal) => Promise<unknown>, cancellation?: AbortSignal): Promise<string[]>;
34
- getStates(scope: AzureWorkItemStatesScope, type: string, read: (signal?: AbortSignal) => Promise<unknown>, cancellation?: AbortSignal): Promise<WorkItemTypeStates>;
35
+ getStates(scope: AzureWorkItemStatesScope, type: string, read: (signal?: AbortSignal) => Promise<unknown>, cancellation?: AbortSignal, readMappings?: (signal?: AbortSignal) => Promise<unknown>): Promise<WorkItemTypeStatesSnapshot>;
35
36
  private read;
36
37
  }
37
38
  //#endregion
@@ -1,20 +1,15 @@
1
1
  import { isCancellationError } from "../../../../utils/cancellation.js";
2
2
  import { PromiseCache } from "../../../../utils/promiseCache.js";
3
3
  import { azureIssueStateCategory, encodeAzurePathSegment } from "./models.js";
4
- import { toAzureWorkItemHtmlFields } from "@gitkraken/provider-apis";
5
4
  //#region ../plus/integrations/src/providers/azure/workItemStateCategories.ts
6
- /** Where an organization's (or a Server collection's) field catalog is read: fields are defined per organization. */
7
- function getAzureWorkItemFieldsPath(namespace) {
8
- return `${encodeAzurePathSegment(namespace)}/_apis/wit/fields?api-version=6.0`;
5
+ function getAzureWorkItemTypesUrl(baseUrl, namespace, project, apiVersion = "6.0") {
6
+ return `${baseUrl.replace(/\/$/, "")}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=${apiVersion}`;
9
7
  }
10
- function getAzureWorkItemTypesUrl(baseUrl, namespace, project) {
11
- return `${baseUrl.replace(/\/$/, "")}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=6.0`;
8
+ function getAzureWorkItemTypeStatesPath(namespace, project, type, apiVersion = "6.0-preview.1") {
9
+ return `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=${apiVersion}`;
12
10
  }
13
- function getAzureWorkItemTypeStatesPath(namespace, project, type) {
14
- return `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=6.0-preview.1`;
15
- }
16
- function getAzureWorkItemTypeStatesUrl(baseUrl, namespace, project, type) {
17
- return `${baseUrl.replace(/\/$/, "")}/${getAzureWorkItemTypeStatesPath(namespace, project, type)}`;
11
+ function getAzureWorkItemTypeStatesUrl(baseUrl, namespace, project, type, apiVersion = "6.0-preview.1") {
12
+ return `${baseUrl.replace(/\/$/, "")}/${getAzureWorkItemTypeStatesPath(namespace, project, type, apiVersion)}`;
18
13
  }
19
14
  function parseWorkItemTypes(body) {
20
15
  const types = body?.value;
@@ -26,17 +21,12 @@ function parseWorkItemTypes(body) {
26
21
  }
27
22
  return [...new Set(names)];
28
23
  }
29
- function parseWorkItemHtmlFields(body) {
30
- const fields = body?.value;
31
- if (!Array.isArray(fields)) throw new Error("Azure DevOps returned no work item fields");
32
- return toAzureWorkItemHtmlFields(fields);
33
- }
34
- function parseWorkItemStates(body) {
24
+ function parseWorkItemStates(body, allowMissingCategory = false) {
35
25
  const states = body?.value;
36
26
  if (!Array.isArray(states) || states.length === 0) throw new Error("Azure DevOps returned no work item states");
37
27
  const parsed = [];
38
28
  for (const state of states) {
39
- if (typeof state?.name !== "string" || !state.name.trim() || typeof state.category !== "string" || azureIssueStateCategory(state.category) == null || parsed.some((s) => s.name === state.name && s.category !== state.category)) throw new Error("Azure DevOps returned an invalid work item state");
29
+ if (typeof state?.name !== "string" || !state.name.trim() || (state.category === void 0 ? !allowMissingCategory : typeof state.category !== "string" || azureIssueStateCategory(state.category) == null) || parsed.some((s) => s.name === state.name && s.category !== state.category)) throw new Error("Azure DevOps returned an invalid work item state");
40
30
  parsed.push({
41
31
  name: state.name,
42
32
  category: state.category
@@ -44,7 +34,40 @@ function parseWorkItemStates(body) {
44
34
  }
45
35
  return parsed;
46
36
  }
37
+ function resolveAzureWorkItemStateCategory(snapshot, state) {
38
+ const matched = snapshot.states.find((s) => s.name === state);
39
+ const category = matched?.category;
40
+ if ((snapshot.usesLegacyMappings || matched != null) && category == null) throw new Error(`Azure DevOps does not expose a category for work item state '${state}'`);
41
+ return category;
42
+ }
43
+ function requireAzureWorkItemStateCategories(snapshot) {
44
+ return snapshot.states.map((state) => {
45
+ if (state.category == null) throw new Error("Azure DevOps does not expose a category for this work item state");
46
+ return {
47
+ name: state.name,
48
+ category: state.category
49
+ };
50
+ });
51
+ }
52
+ function parseWorkItemStateMappings(body) {
53
+ const types = body?.workItemTypeMappedStates;
54
+ if (!Array.isArray(types)) throw new Error("Azure DevOps returned no workflow category mappings");
55
+ const result = /* @__PURE__ */ new Map();
56
+ for (const type of types) {
57
+ if (typeof type?.workItemTypeName !== "string" || !type.workItemTypeName.trim() || type.states == null || typeof type.states !== "object" || Array.isArray(type.states)) throw new Error("Azure DevOps returned invalid workflow category mappings");
58
+ const states = result.get(type.workItemTypeName) ?? /* @__PURE__ */ new Map();
59
+ for (const [name, category] of Object.entries(type.states)) {
60
+ if (!name.trim() || typeof category !== "string" || azureIssueStateCategory(category) == null || states.has(name) && states.get(name) !== category) throw new Error("Azure DevOps returned invalid workflow category mappings");
61
+ states.set(name, category);
62
+ }
63
+ result.set(type.workItemTypeName, states);
64
+ }
65
+ return result;
66
+ }
47
67
  function scopeKey(scope, type) {
68
+ encodeAzurePathSegment(scope.namespace);
69
+ encodeAzurePathSegment(scope.project);
70
+ if (type != null) encodeAzurePathSegment(type);
48
71
  return JSON.stringify([
49
72
  scope.baseUrl.replace(/\/$/, ""),
50
73
  scope.namespace.toLowerCase(),
@@ -55,10 +78,9 @@ function scopeKey(scope, type) {
55
78
  ]);
56
79
  }
57
80
  /**
58
- * Workflow metadata per project, work item type and token, kept for 5 minutes, and each organization's rich text
59
- * fields per token, kept for an hour since they change only when an administrator edits a process. A failed read is
60
- * kept for 30 seconds, so the pages and per-type reads of a listing fail fast with the same error instead of each
61
- * asking a failing server again; the first read after that retries.
81
+ * Workflow metadata per project, work item type and token, kept for 5 minutes. A failed read is kept for 30 seconds,
82
+ * so the pages and per-type reads of a listing fail fast with the same error instead of each asking a failing server
83
+ * again; the first read after that retries.
62
84
  */
63
85
  var AzureWorkItemStatesCache = class {
64
86
  types = new PromiseCache({
@@ -69,31 +91,34 @@ var AzureWorkItemStatesCache = class {
69
91
  createTTL: 3e5,
70
92
  capacity: 256
71
93
  });
72
- htmlFields = new PromiseCache({
73
- createTTL: 36e5,
74
- capacity: 64
94
+ mappings = new PromiseCache({
95
+ createTTL: 3e5,
96
+ capacity: 256
75
97
  });
76
98
  clear() {
77
99
  this.types.clear();
78
100
  this.states.clear();
79
- this.htmlFields.clear();
80
- }
81
- /**
82
- * The rich text fields of the scope's organization that may stand in for a work item's description, in the order
83
- * provider-apis' `getAzureWorkItemDescription` tries them. The scope's project is ignored: fields are defined per
84
- * organization.
85
- */
86
- getHtmlFields(scope, read, cancellation) {
87
- return this.read(this.htmlFields, scopeKey({
88
- ...scope,
89
- project: ""
90
- }), async (signal) => parseWorkItemHtmlFields(await read(signal)), cancellation);
101
+ this.mappings.clear();
91
102
  }
92
103
  getTypes(scope, read, cancellation) {
93
104
  return this.read(this.types, scopeKey(scope), async (signal) => parseWorkItemTypes(await read(signal)), cancellation);
94
105
  }
95
- getStates(scope, type, read, cancellation) {
96
- return this.read(this.states, scopeKey(scope, type), async (signal) => parseWorkItemStates(await read(signal)), cancellation);
106
+ getStates(scope, type, read, cancellation, readMappings) {
107
+ return this.read(this.states, scopeKey(scope, type), async (signal) => {
108
+ const states = parseWorkItemStates(await read(signal), readMappings != null);
109
+ if (readMappings == null || states.every((state) => state.category != null)) return {
110
+ states,
111
+ usesLegacyMappings: false
112
+ };
113
+ const mapped = (await this.read(this.mappings, scopeKey(scope), async (aggregate) => parseWorkItemStateMappings(await readMappings(aggregate)), signal)).get(type);
114
+ return {
115
+ states: states.map((state) => ({
116
+ name: state.name,
117
+ category: state.category ?? mapped?.get(state.name)
118
+ })),
119
+ usesLegacyMappings: true
120
+ };
121
+ }, cancellation);
97
122
  }
98
123
  async read(cache, key, load, cancellation) {
99
124
  const result = await cache.getOrCreate(key, async (_cacheable, signal) => ({
@@ -115,6 +140,6 @@ var AzureWorkItemStatesCache = class {
115
140
  }
116
141
  };
117
142
  //#endregion
118
- export { AzureWorkItemStatesCache, getAzureWorkItemFieldsPath, getAzureWorkItemTypeStatesPath, getAzureWorkItemTypeStatesUrl, getAzureWorkItemTypesUrl };
143
+ export { AzureWorkItemStatesCache, getAzureWorkItemTypeStatesPath, getAzureWorkItemTypeStatesUrl, getAzureWorkItemTypesUrl, requireAzureWorkItemStateCategories, resolveAzureWorkItemStateCategory };
119
144
 
120
145
  //# sourceMappingURL=workItemStateCategories.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workItemStateCategories.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemStateCategories.ts"],"sourcesContent":["import type { AzureWorkItemHtmlField, AzureWorkItemTypeStates } from '@gitkraken/provider-apis';\nimport { toAzureWorkItemHtmlFields } from '@gitkraken/provider-apis';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { AzureWorkItemStateCategory } from './models.js';\nimport { azureIssueStateCategory, encodeAzurePathSegment } from './models.js';\n\ntype WorkItemTypeStates = NonNullable<AzureWorkItemTypeStates['types'][number]['states']>;\n\nexport type AzureWorkItemStatesScope = {\n\tbaseUrl: string;\n\tnamespace: string;\n\tproject: string;\n\ttoken: string;\n\tisPAT: boolean;\n};\n\n/** Where an organization's (or a Server collection's) field catalog is read: fields are defined per organization. */\nexport function getAzureWorkItemFieldsPath(namespace: string): string {\n\treturn `${encodeAzurePathSegment(namespace)}/_apis/wit/fields?api-version=6.0`;\n}\n\nexport function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=6.0`;\n}\n\n// Server 2020 serves this endpoint only as a preview; Services accepts the same version.\nexport function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string): string {\n\treturn `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=6.0-preview.1`;\n}\n\nexport function getAzureWorkItemTypeStatesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${getAzureWorkItemTypeStatesPath(namespace, project, type)}`;\n}\n\nfunction parseWorkItemTypes(body: unknown): string[] {\n\tconst types = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no work item types');\n\n\tconst names: string[] = [];\n\tfor (const type of types as { name?: unknown }[]) {\n\t\tif (typeof type?.name !== 'string' || !type.name.trim()) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item type');\n\t\t}\n\n\t\tnames.push(type.name);\n\t}\n\treturn [...new Set(names)];\n}\n\nfunction parseWorkItemHtmlFields(body: unknown): AzureWorkItemHtmlField[] {\n\tconst fields = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(fields)) throw new Error('Azure DevOps returned no work item fields');\n\n\treturn toAzureWorkItemHtmlFields(fields);\n}\n\nfunction parseWorkItemStates(body: unknown): WorkItemTypeStates {\n\tconst states = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(states) || states.length === 0) throw new Error('Azure DevOps returned no work item states');\n\n\tconst parsed: { name: string; category: string }[] = [];\n\tfor (const state of states as { name?: unknown; category?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof state?.name !== 'string' ||\n\t\t\t!state.name.trim() ||\n\t\t\ttypeof state.category !== 'string' ||\n\t\t\tazureIssueStateCategory(state.category as AzureWorkItemStateCategory) == null ||\n\t\t\tparsed.some(s => s.name === state.name && s.category !== state.category)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item state');\n\t\t}\n\n\t\tparsed.push({ name: state.name, category: state.category });\n\t}\n\treturn parsed;\n}\n\nfunction scopeKey(scope: AzureWorkItemStatesScope, type?: string): string {\n\treturn JSON.stringify([\n\t\tscope.baseUrl.replace(/\\/$/, ''),\n\t\tscope.namespace.toLowerCase(),\n\t\tscope.project.toLowerCase(),\n\t\ttype?.toLowerCase(),\n\t\tscope.token,\n\t\tscope.isPAT,\n\t]);\n}\n\n/**\n * Workflow metadata per project, work item type and token, kept for 5 minutes, and each organization's rich text\n * fields per token, kept for an hour since they change only when an administrator edits a process. A failed read is\n * kept for 30 seconds, so the pages and per-type reads of a listing fail fast with the same error instead of each\n * asking a failing server again; the first read after that retries.\n */\nexport class AzureWorkItemStatesCache {\n\tprivate readonly types = new PromiseCache<string, PromiseSettledResult<string[]>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\tprivate readonly states = new PromiseCache<string, PromiseSettledResult<WorkItemTypeStates>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\tprivate readonly htmlFields = new PromiseCache<string, PromiseSettledResult<AzureWorkItemHtmlField[]>>({\n\t\tcreateTTL: 60 * 60 * 1000,\n\t\tcapacity: 64,\n\t});\n\n\tclear(): void {\n\t\tthis.types.clear();\n\t\tthis.states.clear();\n\t\tthis.htmlFields.clear();\n\t}\n\n\t/**\n\t * The rich text fields of the scope's organization that may stand in for a work item's description, in the order\n\t * provider-apis' `getAzureWorkItemDescription` tries them. The scope's project is ignored: fields are defined per\n\t * organization.\n\t */\n\tgetHtmlFields(\n\t\tscope: Omit<AzureWorkItemStatesScope, 'project'>,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<AzureWorkItemHtmlField[]> {\n\t\treturn this.read(\n\t\t\tthis.htmlFields,\n\t\t\tscopeKey({ ...scope, project: '' }),\n\t\t\tasync signal => parseWorkItemHtmlFields(await read(signal)),\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tgetTypes(\n\t\tscope: AzureWorkItemStatesScope,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<string[]> {\n\t\treturn this.read(\n\t\t\tthis.types,\n\t\t\tscopeKey(scope),\n\t\t\tasync signal => parseWorkItemTypes(await read(signal)),\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tgetStates(\n\t\tscope: AzureWorkItemStatesScope,\n\t\ttype: string,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<WorkItemTypeStates> {\n\t\treturn this.read(\n\t\t\tthis.states,\n\t\t\tscopeKey(scope, type),\n\t\t\tasync signal => parseWorkItemStates(await read(signal)),\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async read<T>(\n\t\tcache: PromiseCache<string, PromiseSettledResult<T>>,\n\t\tkey: string,\n\t\tload: (signal?: AbortSignal) => Promise<T>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<T> {\n\t\tconst result = await cache.getOrCreate(\n\t\t\tkey,\n\t\t\tasync (_cacheable, signal) => ({ status: 'fulfilled', value: await load(signal) }),\n\t\t\t{\n\t\t\t\tcancellation: cancellation,\n\t\t\t\t// Every caller must still see the failure; caching undefined would turn later pages into successes.\n\t\t\t\tonError: error =>\n\t\t\t\t\tisCancellationError(error) || cache.get(key) == null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: { value: { status: 'rejected', reason: error }, createTTL: 30 * 1000, suppress: true },\n\t\t\t},\n\t\t);\n\t\tif (result.status === 'rejected') throw result.reason;\n\n\t\treturn result.value;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workItemStateCategories.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemStateCategories.ts"],"sourcesContent":["import type { AzureWorkItemTypeStates } from '@gitkraken/provider-apis';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { AzureWorkItemStateCategory } from './models.js';\nimport { azureIssueStateCategory, encodeAzurePathSegment } from './models.js';\n\ntype WorkItemTypeStates = readonly { name: string; category?: string }[];\ntype WorkItemTypeStatesSnapshot = {\n\tstates: WorkItemTypeStates;\n\tusesLegacyMappings: boolean;\n};\ntype WorkItemStateMappings = ReadonlyMap<string, ReadonlyMap<string, string>>;\n\nexport type AzureWorkItemStatesScope = {\n\tbaseUrl: string;\n\tnamespace: string;\n\tproject: string;\n\ttoken: string;\n\tisPAT: boolean;\n};\n\nexport function getAzureWorkItemTypesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\tapiVersion = '6.0',\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=${apiVersion}`;\n}\n\n// Server 2020 serves this endpoint only as a preview; Services accepts the same version.\nexport function getAzureWorkItemTypeStatesPath(\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n\tapiVersion = '6.0-preview.1',\n): string {\n\treturn `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=${apiVersion}`;\n}\n\nexport function getAzureWorkItemTypeStatesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n\tapiVersion = '6.0-preview.1',\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${getAzureWorkItemTypeStatesPath(namespace, project, type, apiVersion)}`;\n}\n\nfunction parseWorkItemTypes(body: unknown): string[] {\n\tconst types = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no work item types');\n\n\tconst names: string[] = [];\n\tfor (const type of types as { name?: unknown }[]) {\n\t\tif (typeof type?.name !== 'string' || !type.name.trim()) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item type');\n\t\t}\n\n\t\tnames.push(type.name);\n\t}\n\treturn [...new Set(names)];\n}\n\nfunction parseWorkItemStates(body: unknown, allowMissingCategory = false): WorkItemTypeStates {\n\tconst states = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(states) || states.length === 0) throw new Error('Azure DevOps returned no work item states');\n\n\tconst parsed: { name: string; category?: string }[] = [];\n\tfor (const state of states as { name?: unknown; category?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof state?.name !== 'string' ||\n\t\t\t!state.name.trim() ||\n\t\t\t(state.category === undefined\n\t\t\t\t? !allowMissingCategory\n\t\t\t\t: typeof state.category !== 'string' ||\n\t\t\t\t\tazureIssueStateCategory(state.category as AzureWorkItemStateCategory) == null) ||\n\t\t\tparsed.some(s => s.name === state.name && s.category !== state.category)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item state');\n\t\t}\n\n\t\tparsed.push({ name: state.name, category: state.category as string | undefined });\n\t}\n\treturn parsed;\n}\n\nexport function resolveAzureWorkItemStateCategory(\n\tsnapshot: WorkItemTypeStatesSnapshot,\n\tstate: string,\n): AzureWorkItemStateCategory | undefined {\n\tconst matched = snapshot.states.find(s => s.name === state);\n\tconst category = matched?.category as AzureWorkItemStateCategory | undefined;\n\tif ((snapshot.usesLegacyMappings || matched != null) && category == null) {\n\t\tthrow new Error(`Azure DevOps does not expose a category for work item state '${state}'`);\n\t}\n\n\treturn category;\n}\n\nexport function requireAzureWorkItemStateCategories(\n\tsnapshot: WorkItemTypeStatesSnapshot,\n): NonNullable<AzureWorkItemTypeStates['types'][number]['states']> {\n\treturn snapshot.states.map(state => {\n\t\tif (state.category == null) throw new Error('Azure DevOps does not expose a category for this work item state');\n\n\t\treturn { name: state.name, category: state.category };\n\t});\n}\n\nfunction parseWorkItemStateMappings(body: unknown): WorkItemStateMappings {\n\tconst types = (body as { workItemTypeMappedStates?: unknown } | null)?.workItemTypeMappedStates;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no workflow category mappings');\n\n\tconst result = new Map<string, Map<string, string>>();\n\tfor (const type of types as { workItemTypeName?: unknown; states?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof type?.workItemTypeName !== 'string' ||\n\t\t\t!type.workItemTypeName.trim() ||\n\t\t\ttype.states == null ||\n\t\t\ttypeof type.states !== 'object' ||\n\t\t\tArray.isArray(type.states)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned invalid workflow category mappings');\n\t\t}\n\n\t\tconst states = result.get(type.workItemTypeName) ?? new Map<string, string>();\n\t\tfor (const [name, category] of Object.entries(type.states)) {\n\t\t\tif (\n\t\t\t\t!name.trim() ||\n\t\t\t\ttypeof category !== 'string' ||\n\t\t\t\tazureIssueStateCategory(category as AzureWorkItemStateCategory) == null ||\n\t\t\t\t(states.has(name) && states.get(name) !== category)\n\t\t\t) {\n\t\t\t\tthrow new Error('Azure DevOps returned invalid workflow category mappings');\n\t\t\t}\n\n\t\t\tstates.set(name, category);\n\t\t}\n\t\tresult.set(type.workItemTypeName, states);\n\t}\n\treturn result;\n}\n\nfunction scopeKey(scope: AzureWorkItemStatesScope, type?: string): string {\n\tencodeAzurePathSegment(scope.namespace);\n\tencodeAzurePathSegment(scope.project);\n\tif (type != null) {\n\t\tencodeAzurePathSegment(type);\n\t}\n\n\treturn JSON.stringify([\n\t\tscope.baseUrl.replace(/\\/$/, ''),\n\t\tscope.namespace.toLowerCase(),\n\t\tscope.project.toLowerCase(),\n\t\ttype?.toLowerCase(),\n\t\tscope.token,\n\t\tscope.isPAT,\n\t]);\n}\n\n/**\n * Workflow metadata per project, work item type and token, kept for 5 minutes. A failed read is kept for 30 seconds,\n * so the pages and per-type reads of a listing fail fast with the same error instead of each asking a failing server\n * again; the first read after that retries.\n */\nexport class AzureWorkItemStatesCache {\n\tprivate readonly types = new PromiseCache<string, PromiseSettledResult<string[]>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\tprivate readonly states = new PromiseCache<string, PromiseSettledResult<WorkItemTypeStatesSnapshot>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tprivate readonly mappings = new PromiseCache<string, PromiseSettledResult<WorkItemStateMappings>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tclear(): void {\n\t\tthis.types.clear();\n\t\tthis.states.clear();\n\t\tthis.mappings.clear();\n\t}\n\n\tgetTypes(\n\t\tscope: AzureWorkItemStatesScope,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<string[]> {\n\t\treturn this.read(\n\t\t\tthis.types,\n\t\t\tscopeKey(scope),\n\t\t\tasync signal => parseWorkItemTypes(await read(signal)),\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tgetStates(\n\t\tscope: AzureWorkItemStatesScope,\n\t\ttype: string,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t\treadMappings?: (signal?: AbortSignal) => Promise<unknown>,\n\t): Promise<WorkItemTypeStatesSnapshot> {\n\t\treturn this.read(\n\t\t\tthis.states,\n\t\t\tscopeKey(scope, type),\n\t\t\tasync signal => {\n\t\t\t\tconst states = parseWorkItemStates(await read(signal), readMappings != null);\n\t\t\t\tif (readMappings == null || states.every(state => state.category != null)) {\n\t\t\t\t\treturn { states: states, usesLegacyMappings: false };\n\t\t\t\t}\n\n\t\t\t\tconst mappings = await this.read(\n\t\t\t\t\tthis.mappings,\n\t\t\t\t\tscopeKey(scope),\n\t\t\t\t\tasync aggregate => parseWorkItemStateMappings(await readMappings(aggregate)),\n\t\t\t\t\tsignal,\n\t\t\t\t);\n\t\t\t\tconst mapped = mappings.get(type);\n\t\t\t\treturn {\n\t\t\t\t\tstates: states.map(state => ({\n\t\t\t\t\t\tname: state.name,\n\t\t\t\t\t\tcategory: state.category ?? mapped?.get(state.name),\n\t\t\t\t\t})),\n\t\t\t\t\tusesLegacyMappings: true,\n\t\t\t\t};\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async read<T>(\n\t\tcache: PromiseCache<string, PromiseSettledResult<T>>,\n\t\tkey: string,\n\t\tload: (signal?: AbortSignal) => Promise<T>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<T> {\n\t\tconst result = await cache.getOrCreate(\n\t\t\tkey,\n\t\t\tasync (_cacheable, signal) => ({ status: 'fulfilled', value: await load(signal) }),\n\t\t\t{\n\t\t\t\tcancellation: cancellation,\n\t\t\t\t// Every caller must still see the failure; caching undefined would turn later pages into successes.\n\t\t\t\tonError: error =>\n\t\t\t\t\tisCancellationError(error) || cache.get(key) == null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: { value: { status: 'rejected', reason: error }, createTTL: 30 * 1000, suppress: true },\n\t\t\t},\n\t\t);\n\t\tif (result.status === 'rejected') throw result.reason;\n\n\t\treturn result.value;\n\t}\n}\n"],"mappings":"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"}