@gitkraken/core-gitlens 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -1
- package/dist/git/cache.d.ts +84 -29
- package/dist/git/cache.js +188 -54
- package/dist/git/cache.js.map +1 -1
- package/dist/git/features.d.ts +1 -1
- package/dist/git/features.js +4 -0
- package/dist/git/features.js.map +1 -1
- package/dist/git/models/branch.d.ts +15 -2
- package/dist/git/models/branch.js +27 -5
- package/dist/git/models/branch.js.map +1 -1
- package/dist/git/models/status.js +1 -1
- package/dist/git/models/worktree.d.ts +4 -1
- package/dist/git/models/worktree.js +4 -2
- package/dist/git/models/worktree.js.map +1 -1
- package/dist/git/providers/branches.d.ts +73 -8
- package/dist/git/providers/pausedOperations.d.ts +6 -0
- package/dist/git/providers/provider.d.ts +34 -2
- package/dist/git/providers/status.d.ts +8 -2
- package/dist/git/remotes/azure-devops.js +61 -53
- package/dist/git/remotes/azure-devops.js.map +1 -1
- package/dist/git/service.d.ts +8 -5
- package/dist/git/service.js +16 -8
- package/dist/git/service.js.map +1 -1
- package/dist/git/utils/branch.utils.d.ts +8 -1
- package/dist/git/utils/branch.utils.js +30 -1
- package/dist/git/utils/branch.utils.js.map +1 -1
- package/dist/git/watching/changeEvent.d.ts +13 -2
- package/dist/git/watching/changeEvent.js +17 -3
- package/dist/git/watching/changeEvent.js.map +1 -1
- package/dist/git/watching/gitDirPoller.d.ts +111 -0
- package/dist/git/watching/gitDirPoller.js +266 -0
- package/dist/git/watching/gitDirPoller.js.map +1 -0
- package/dist/git/watching/provider.d.ts +16 -0
- package/dist/git/watching/watchGroup.d.ts +40 -4
- package/dist/git/watching/watchGroup.js +143 -17
- package/dist/git/watching/watchGroup.js.map +1 -1
- package/dist/git/watching/watchService.d.ts +70 -1
- package/dist/git/watching/watchService.js +158 -13
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +44 -0
- package/dist/git/watching/watchSession.js +91 -6
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.d.ts +21 -0
- package/dist/git/watching/watcherPatterns.js +67 -1
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git/watching/worktreeSummary.d.ts +34 -7
- package/dist/git/watching/worktreeSummary.js +39 -12
- package/dist/git/watching/worktreeSummary.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +34 -2
- package/dist/git-cli/cliGitProvider.js +119 -17
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/commandClassifier.d.ts +15 -0
- package/dist/git-cli/exec/commandClassifier.js +48 -2
- package/dist/git-cli/exec/commandClassifier.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +15 -3
- package/dist/git-cli/exec/git.js +38 -17
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/gitQueue.d.ts +2 -0
- package/dist/git-cli/exec/gitQueue.js +2 -1
- package/dist/git-cli/exec/gitQueue.js.map +1 -1
- package/dist/git-cli/exec/indexLock.d.ts +38 -0
- package/dist/git-cli/exec/indexLock.js +150 -0
- package/dist/git-cli/exec/indexLock.js.map +1 -0
- package/dist/git-cli/parsers/worktreeParser.js +42 -2
- package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +109 -10
- package/dist/git-cli/providers/branches.js +624 -131
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts +9 -5
- package/dist/git-cli/providers/config.js +60 -27
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +17 -0
- package/dist/git-cli/providers/operations.js +153 -96
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.d.ts +8 -0
- package/dist/git-cli/providers/pausedOperations.js +60 -40
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.js +11 -8
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/remotes.js +19 -10
- package/dist/git-cli/providers/remotes.js.map +1 -1
- package/dist/git-cli/providers/staging.js +40 -15
- package/dist/git-cli/providers/staging.js.map +1 -1
- package/dist/git-cli/providers/stash.d.ts +2 -0
- package/dist/git-cli/providers/stash.js +33 -18
- package/dist/git-cli/providers/stash.js.map +1 -1
- package/dist/git-cli/providers/status.d.ts +3 -1
- package/dist/git-cli/providers/status.js +38 -20
- package/dist/git-cli/providers/status.js.map +1 -1
- package/dist/git-cli/providers/tags.js +6 -4
- package/dist/git-cli/providers/tags.js.map +1 -1
- package/dist/git-cli/providers/worktrees.d.ts +1 -0
- package/dist/git-cli/providers/worktrees.js +60 -31
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/git-cli/service.d.ts +6 -0
- package/dist/git-cli/service.js +5 -2
- package/dist/git-cli/service.js.map +1 -1
- package/dist/ipc/discovery.js +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -1
- package/dist/plus/git-github/providers/github/status.d.ts +3 -1
- package/dist/plus/git-github/providers/github/status.js +1 -1
- package/dist/plus/git-github/providers/github/status.js.map +1 -1
- package/dist/plus/integrations/collectionMetadata.d.ts +13 -2
- package/dist/plus/integrations/collectionMetadata.js +14 -2
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +2 -2
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +1 -0
- package/dist/plus/integrations/integrationService.js +1 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +1 -0
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +41 -5
- package/dist/plus/integrations/models/gitHostIntegration.js +106 -29
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +17 -4
- package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
- package/dist/plus/integrations/providers/apiClients.js +7 -1
- package/dist/plus/integrations/providers/apiClients.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.d.ts +5 -4
- package/dist/plus/integrations/providers/azure/azure.js +37 -77
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.utils.js +20 -17
- package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -1
- package/dist/plus/integrations/providers/azure/models.d.ts +2 -2
- package/dist/plus/integrations/providers/azure/models.js +4 -4
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +18 -14
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js +69 -17
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.d.ts +44 -0
- package/dist/plus/integrations/providers/azureDevOps.js +108 -17
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +45 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.js +121 -6
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
- package/dist/plus/integrations/providers/jira-server.js +18 -14
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.js +18 -14
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +1 -2
- package/dist/plus/integrations/providers/models.js +9 -2
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +7 -17
- package/dist/plus/integrations/providers/providersApi.js +85 -78
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +22 -0
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +69 -9
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.js +8 -2
- package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
- package/dist/plus/integrations/reads/filters.d.ts +3 -3
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/issueBatch.d.ts +2 -1
- package/dist/plus/integrations/reads/issueBatch.js +1 -1
- package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +23 -14
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts +2 -1
- package/dist/plus/integrations/reads/issues.js +18 -3
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequestBatch.js +1 -1
- package/dist/plus/integrations/reads/resolveRepository.js +12 -3
- package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +11 -4
- package/dist/plus/integrations/reads/warnings.js +15 -5
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +36 -2
- package/dist/plus/integrations/results.js +33 -13
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/utils/env/node/exec.js +2 -2
- package/dist/utils/promiseCache.d.ts +10 -0
- package/dist/utils/promiseCache.js +17 -2
- package/dist/utils/promiseCache.js.map +1 -1
- package/docs/integrations.md +52 -20
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/models.ts"],"sourcesContent":["import 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 { pullRequestRevision } from '../../models/integration.js';\nimport type { ProviderAccount, ProviderIssue } from '../models.js';\nimport {\n\tfromProviderPullRequestMergeableState,\n\tfromProviderPullRequestState,\n\tGitPullRequestMergeableState,\n\tGitPullRequestReviewState,\n\tGitPullRequestState,\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): IssueOrPullRequestState {\n\treturn isClosedAzureWorkItemStateCategory(category) ? 'closed' : 'opened';\n}\n\nexport interface AzureLink {\n\thref: string;\n}\n\nexport interface AzureUser {\n\tdisplayName: string;\n\turl: string;\n\t_links: {\n\t\tavatar: AzureLink;\n\t};\n\tid: string;\n\tuniqueName: string;\n\timageUrl: string;\n\tdescriptor?: string;\n}\n\nexport interface AzureUserWithVote extends AzureUser {\n\tisFlagged?: boolean;\n\thasDeclined?: boolean;\n\tisReapprove?: boolean;\n\tisRequired?: boolean;\n\tvote?: AzurePullRequestVote;\n}\n\nexport type AzurePullRequestVote =\n\t| 10 // approved\n\t| 5 // approved with suggestions\n\t| 0 // no vote\n\t| -5 // waiting for author\n\t| -10; // rejected\n\nexport interface AzureWorkItemCommentVersionRef {\n\tcommentId: number;\n\tcreatedInRevision: number;\n\tisDeleted: boolean;\n\ttext: string;\n\turl: string;\n\tversion: number;\n}\n\nexport interface AzureWorkItemRelation {\n\tattributes: {\n\t\t[key: string]: string;\n\t};\n\trelation: string;\n\turl: string;\n}\n\nexport interface WorkItem {\n\t_links: {\n\t\tfields: AzureLink;\n\t\thtml: AzureLink;\n\t\tself: AzureLink;\n\t\tworkItemComments: AzureLink;\n\t\tworkItemRevisions: AzureLink;\n\t\tworkItemType: AzureLink;\n\t\tworkItemUpdates: AzureLink;\n\t};\n\tfields: {\n\t\t//'System.AreaPath': string;\n\t\t'System.TeamProject': string;\n\t\t'System.IterationPath'?: string;\n\t\t'System.WorkItemType': string;\n\t\t'System.State': string;\n\t\t// 'System.Reason': string;\n\t\t'System.AssignedTo': AzureUser;\n\t\t'System.CreatedDate': string;\n\t\t'System.CreatedBy': AzureUser;\n\t\t'System.ChangedDate': string;\n\t\t'System.ChangedBy': AzureUser;\n\t\t'System.CommentCount': number;\n\t\t'System.Description': string;\n\t\t'System.Title': string;\n\t\t'Microsoft.VSTS.Common.ClosedDate': string;\n\t\t// 'Microsoft.VSTS.Common.StateChangeDate': string;\n\t\t// 'Microsoft.VSTS.Common.Priority': number;\n\t\t// 'Microsoft.VSTS.Common.Severity': string;\n\t\t// 'Microsoft.VSTS.Common.ValueArea': string;\n\t};\n\tid: number;\n\trev: number;\n\turl: string;\n\tcommentVersionRef?: AzureWorkItemCommentVersionRef;\n\trelations?: AzureWorkItemRelation[];\n}\n\nexport interface AzureWorkItemState {\n\tname: string;\n\tcolor: string;\n\tcategory: AzureWorkItemStateCategory;\n}\n\nexport type AzurePullRequestStatus = 'abandoned' | 'active' | 'completed' | 'notSet';\nexport function azurePullRequestStatusToState(status: AzurePullRequestStatus): IssueOrPullRequestState {\n\tswitch (status) {\n\t\tcase 'abandoned':\n\t\t\treturn 'closed';\n\t\tcase 'completed':\n\t\t\treturn 'merged';\n\t\tcase 'active':\n\t\tcase 'notSet':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\nexport function isClosedAzurePullRequestStatus(status: AzurePullRequestStatus): boolean {\n\treturn azurePullRequestStatusToState(status) !== 'opened';\n}\n\nexport type AzureProjectState = 'createPending' | 'deleted' | 'deleting' | 'new' | 'unchanged' | 'wellFormed';\nexport type AzureProjectVisibility = 'private' | 'public';\n\nexport interface AzureProject {\n\tid: string;\n\tname: string;\n\turl: string;\n\tstate: AzureProjectState;\n\trevision: number;\n\tvisibility: AzureProjectVisibility;\n\tlastUpdateTime: string;\n}\n\nexport interface AzureRepositoryReference {\n\tid: string;\n\tname: string;\n\turl: string;\n\tremoteUrl?: string;\n}\n\nexport interface AzureRepository extends AzureRepositoryReference {\n\tproject: AzureProject;\n\tsize: number;\n\tremoteUrl: string;\n\tsshUrl: string;\n\twebUrl: string;\n\tisDisabled: boolean;\n\tisInMaintenance: boolean;\n}\n\nexport interface AzurePullRequestRepository extends AzureRepositoryReference {\n\tproject: Pick<AzureProject, 'id' | 'name'>;\n}\n\n/** The URL fields of a repository response — all the fork lookup reads, and all an older `api-version` promises. */\nexport type AzureRepositoryUrls = Partial<Pick<AzureRepository, 'webUrl' | 'remoteUrl'>>;\n\n/** The `GET .../_apis/git/repositories/{repositoryId or name}` response, adding fork/default-branch fields to {@link AzureRepository}. */\nexport interface AzureRepositoryWithMetadata extends AzureRepository {\n\t/** Fully-qualified ref, e.g. `refs/heads/main`. */\n\tdefaultBranch?: string;\n\tisFork?: boolean;\n\tparentRepository?: {\n\t\tid: string;\n\t\tname?: string;\n\t\tproject?: { id: string; name: string };\n\t};\n}\n\nexport interface AzureGitUser {\n\tdate?: string;\n\temail?: string;\n\timageUrl?: string;\n\tname: string;\n}\n\nexport interface AzureGitCommitRef {\n\tcommitId: string;\n\turl: string;\n}\n\nexport interface AzureGitCommit {\n\t_links: {\n\t\tchanges: AzureLink;\n\t\trepository: AzureLink;\n\t\tself: AzureLink;\n\t\tweb: AzureLink;\n\t};\n\tauthor: AzureGitUser;\n\tcomment: string;\n\tcommentTruncated?: boolean;\n\tcommitId: string;\n\tcommitTooManyChanges?: boolean;\n\tcommitter: AzureGitUser;\n\tparents: string[];\n\tpush: {\n\t\tdate: string;\n\t\tpushedBy: AzureUser;\n\t\tpushId: number;\n\t};\n\tremoteUrl: string;\n\tstatuses?: AzureGitStatus[];\n\ttreeId: string;\n\turl: string;\n\tworkItems?: AzureResourceRef[];\n}\n\nexport interface AzureResourceRef {\n\tid: string;\n\turl: string;\n}\n\nexport interface AzurePullRequestCompletionOptions {\n\tautoCompleteIgnoreConflicts: number[];\n\tbypassPolicy: boolean;\n\tbypassReason: string;\n\tdeleteSourceBranch: boolean;\n\tmergeCommitMessage: string;\n\tmergeStrategy: 'noFastForward' | 'rebase' | 'rebaseMerge' | 'squash';\n\tsquashMerge: boolean;\n\ttransitionWorkItems: boolean;\n\ttriggeredByAutoComplete: boolean;\n}\n\nexport interface AzureGitStatus {\n\tcontext: {\n\t\tname: string;\n\t\tgenre: string;\n\t};\n\tcreatedBy: AzureUser;\n\tcreateDate: string;\n\tdescription: string;\n\tstate: 'error' | 'failed' | 'notApplicable' | 'notSet' | 'pending' | 'succeeded';\n\ttargetUrl: string;\n\tupdateDate: string;\n}\n\nexport interface AzureGitForkRef {\n\tcreator: AzureUser;\n\tisLocked: boolean;\n\tisLockedBy: AzureUser;\n\tname: string;\n\tobjectId: string;\n\tpeeledObjectId: string;\n\trepository: AzureRepositoryReference;\n\tstatuses: AzureGitStatus[];\n\turl: string;\n}\n\nexport interface AzureWebApiTagDefinition {\n\tactive: boolean;\n\tid: string;\n\tname: string;\n\turl: string;\n}\n\nexport interface AzureGitPullRequestMergeOptions {\n\tconflictAuthorshipCommits: boolean;\n\tdetectRenameFalsePositives: boolean;\n\tdisableRenames: boolean;\n}\n\nexport type AzurePullRequestAsyncStatus =\n\t| 'conflicts'\n\t| 'failure'\n\t| 'notSet'\n\t| 'queued'\n\t| 'rejectedByPolicy'\n\t| 'succeeded';\n\nexport interface AzurePullRequest {\n\trepository: AzurePullRequestRepository;\n\tpullRequestId: number;\n\tcodeReviewId: number;\n\tstatus: AzurePullRequestStatus;\n\tcreatedBy: AzureUser;\n\tcreationDate: string;\n\tclosedDate?: string;\n\tclosedBy?: AzureUser; // Can be missed even if closedDate is presented.\n\ttitle: string;\n\tdescription: string;\n\tforkSource?: AzureGitForkRef;\n\tsourceRefName: string;\n\ttargetRefName: string;\n\tisDraft: boolean;\n\tmergeId: string;\n\tmergeStatus?: AzurePullRequestAsyncStatus;\n\tlastMergeCommit?: AzureGitCommitRef;\n\tlastMergeSourceCommit: AzureGitCommitRef;\n\tlastMergeTargetCommit: AzureGitCommitRef;\n\treviewers: AzureUserWithVote[];\n\turl: string;\n\tsupportsIterations: boolean;\n}\n\nexport interface AzurePullRequestWithLinks extends AzurePullRequest {\n\t_links: {\n\t\tself: AzureLink;\n\t\trepository: AzureLink;\n\t\tworkItems: AzureLink;\n\t\tsourceBranch: AzureLink;\n\t\ttargetBranch: AzureLink;\n\t\tstatuses: AzureLink;\n\t\tsourceCommit: AzureLink;\n\t\ttargetCommit: AzureLink;\n\t\tcreatedBy: AzureLink;\n\t\titerations: AzureLink;\n\t};\n\tartifactId: string;\n\tautoCompleteSetBy?: AzureUser;\n\tcommits?: AzureGitCommitRef[];\n\tcompletionOptions?: AzurePullRequestCompletionOptions;\n\tcompletionQueueTime?: string;\n\thasMultipleMergeBases?: boolean;\n\tlabels?: AzureWebApiTagDefinition[];\n\tmergeFailureMessage?: string;\n\tmergeFailureType?: 'caseSensitive' | 'none' | 'objectTooLarge' | 'unknown';\n\tmergeOptions?: AzureGitPullRequestMergeOptions;\n\tremoteUrl?: string;\n\tworkItemRefs?: AzureResourceRef[];\n}\n\nexport function getAzureDevOpsOwner(url: URL): string {\n\treturn url.pathname.split('/')[1];\n}\nexport function isVsts(domain: string): boolean {\n\treturn domain.endsWith(vstsHostnameSuffix);\n}\n\n/**\n * Encodes one route segment of an Azure DevOps request (#5878): `encodeURIComponent` leaves a dot segment as is, and\n * the URL would resolve it away, re-scoping the request instead of failing it.\n */\nexport function encodeAzurePathSegment(value: string): string {\n\tif (value === '.' || value === '..') throw new Error(`Invalid Azure path segment '${value}'.`);\n\n\treturn encodeURIComponent(value);\n}\n\n/**\n * `baseUrl` and `owner` are the authoritative prefix used for the API request. The payload URL cannot supply that\n * prefix safely because it may address the repository by id or name an untrusted host.\n */\nfunction getAzureRepositoryWebUrl(baseUrl: string, owner: string, projectName: string, repoName: string): string {\n\tconst repoPath = `${encodeURIComponent(owner)}/${encodeURIComponent(projectName)}/_git/${encodeURIComponent(repoName)}`;\n\treturn `${baseUrl.replace(/\\/+$/, '')}/${repoPath}`;\n}\n\nexport function getAzurePullRequestWebUrl(pr: AzurePullRequest, baseUrl: string, owner: string): string {\n\tconst repoUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\treturn `${repoUrl}/pullrequest/${pr.pullRequestId}`;\n}\n\n/**\n * Whether `url` and `expected` are the two cloud spellings of one organization: a cloud organization answers on both\n * `dev.azure.com/{owner}` and the legacy `{owner}.visualstudio.com`, so those name the same host. Anything else is\n * somewhere we didn't ask.\n */\nfunction isSameAzureCloudOrganization(url: URL, expected: URL, expectedOwner: string): boolean {\n\tif (url.protocol !== expected.protocol) return false;\n\n\tconst owner = expectedOwner.toLowerCase();\n\tif (expected.hostname !== 'dev.azure.com' && !isVsts(expected.hostname)) return false;\n\n\tif (url.hostname === 'dev.azure.com') return getAzureDevOpsOwner(url).toLowerCase() === owner;\n\n\treturn url.hostname === `${owner}${vstsHostnameSuffix}`;\n}\n\n/**\n * Whether `url` sits under the collection the integration is configured to talk to — `{expectedUrl}/{expectedOwner}`.\n * A matching origin says nothing on its own: Azure organizations and self-hosted collections are path segments.\n */\nfunction isUnderAzureCollection(url: URL, expected: URL, expectedOwner: string): boolean {\n\tconst base = expected.pathname.replace(/\\/+$/, '');\n\tconst prefix = `${base}/${encodeURIComponent(expectedOwner)}/`.toLowerCase();\n\treturn `${url.pathname}/`.toLowerCase().startsWith(prefix);\n}\n\n/**\n * Restricts a provider-supplied repository URL to the integration's collection and removes credentials, query, and\n * fragment before a consumer can use it as a git remote. Takes the value as the payloads carry it — every one of\n * these URLs is optional — so a caller never has to spell the absent case itself.\n */\nexport function sanitizeAzureRepositoryUrl(\n\tvalue: string | undefined,\n\texpectedUrl: string,\n\texpectedOwner: string,\n): string | undefined {\n\tif (value == null) return undefined;\n\n\tlet expected: URL;\n\tlet url: URL;\n\ttry {\n\t\texpected = new URL(expectedUrl);\n\t\turl = new URL(value);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (expected.protocol !== 'https:' && expected.protocol !== 'http:') return undefined;\n\tif (!expectedOwner) return undefined;\n\n\tif (url.origin === expected.origin) {\n\t\tif (!isUnderAzureCollection(url, expected, expectedOwner)) return undefined;\n\t} else if (!isSameAzureCloudOrganization(url, expected, expectedOwner)) {\n\t\treturn undefined;\n\t}\n\n\turl.username = '';\n\turl.password = '';\n\turl.search = '';\n\turl.hash = '';\n\treturn url.toString();\n}\n\n/**\n * The clone URL the payload carries for a repository, restricted to the integration's collection and then\n * cross-checked against the repository that payload names.\n *\n * The collection check alone is not enough for a URL that is READ rather than built. Every other repository URL the\n * model reports is built from the authoritative prefix, so the payload cannot move it; this one cannot be built,\n * because Azure spells a repository for git the same way it spells it for the web only by convention. A URL under\n * the right collection may still name a DIFFERENT repository in it, which would leave `cloneHttps` describing one\n * repository while `url` describes another.\n *\n * Azure ends a clone URL with `_git/{repo}` on every host style — including the short `{owner}/_git/{repo}` form it\n * uses when a repository carries its project's name, and the legacy `{owner}.visualstudio.com` spelling that has no\n * organization segment at all — so the repository name is the one part comparable across all of them. A same-named\n * repository in another project of the same organization is the single substitution this cannot catch.\n */\nfunction getAzureRepositoryCloneUrl(\n\trepository: AzureRepositoryReference,\n\tbaseUrl: string,\n\towner: string,\n): string | undefined {\n\tconst sanitized = sanitizeAzureRepositoryUrl(repository.remoteUrl, baseUrl, owner);\n\tif (sanitized == null) return undefined;\n\n\t// Safe to parse: `sanitizeAzureRepositoryUrl` returns what it already parsed.\n\tconst segments = new URL(sanitized).pathname.split('/').filter(s => s.length > 0);\n\tconst name = segments.pop();\n\tif (name == null || segments.pop() !== '_git') return undefined;\n\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(name);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\t// Azure resolves project and repository names case-insensitively, so comparing them any other way would refuse a\n\t// URL the provider considers the same one.\n\treturn decoded.toLowerCase() === repository.name.toLowerCase() ? sanitized : undefined;\n}\n\nexport function fromAzurePullRequestMergeStatusToMergeableState(\n\tmergeStatus: AzurePullRequestAsyncStatus,\n): PullRequestMergeableState {\n\tswitch (mergeStatus) {\n\t\tcase 'conflicts':\n\t\t\treturn PullRequestMergeableState.Conflicting;\n\t\tcase 'failure':\n\t\t\treturn PullRequestMergeableState.FailingChecks;\n\t\tcase 'rejectedByPolicy':\n\t\t\treturn PullRequestMergeableState.BlockedByPolicy;\n\t\tcase 'succeeded':\n\t\t\treturn PullRequestMergeableState.Mergeable;\n\t\tcase 'notSet':\n\t\tcase 'queued':\n\t\tdefault:\n\t\t\treturn PullRequestMergeableState.Unknown;\n\t}\n}\n\nexport function fromAzurePullRequestVoteToReviewState(vote: AzurePullRequestVote): PullRequestReviewState {\n\tswitch (vote) {\n\t\tcase 10:\n\t\tcase 5:\n\t\t\treturn PullRequestReviewState.Approved;\n\t\tcase 0:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t\tcase -5:\n\t\tcase -10:\n\t\t\treturn PullRequestReviewState.ChangesRequested;\n\t\tdefault:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t}\n}\n\nexport function fromAzureUserWithVoteToReviewer(reviewer: AzureUserWithVote): PullRequestReviewer {\n\treturn {\n\t\tisCodeOwner: undefined,\n\t\treviewer: {\n\t\t\tavatarUrl: reviewer.imageUrl,\n\t\t\tid: reviewer.id,\n\t\t\tname: reviewer.displayName,\n\t\t\tusername: reviewer.uniqueName,\n\t\t\turl: reviewer.url,\n\t\t},\n\t\tstate: fromAzurePullRequestVoteToReviewState(reviewer.vote ?? 0),\n\t};\n}\n\nexport function getAzurePullRequestReviewDecision(\n\tvotes: AzurePullRequestVote[],\n): PullRequestReviewDecision | undefined {\n\tconst reviewStates = votes.map(vote => fromAzurePullRequestVoteToReviewState(vote));\n\tif (reviewStates.includes(PullRequestReviewState.ChangesRequested)) {\n\t\treturn PullRequestReviewDecision.ChangesRequested;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.ReviewRequested)) {\n\t\treturn PullRequestReviewDecision.ReviewRequired;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.Approved)) {\n\t\treturn PullRequestReviewDecision.Approved;\n\t}\n\n\treturn undefined;\n}\n\nexport function fromAzureReviewerToPullRequestMember(reviewer: AzureUser): PullRequestMember {\n\treturn {\n\t\tavatarUrl: reviewer.imageUrl,\n\t\tid: reviewer.id,\n\t\tname: reviewer.displayName,\n\t\tusername: reviewer.uniqueName,\n\t\turl: reviewer.url,\n\t};\n}\n\nexport function normalizeAzureBranchName(branchName: string): string {\n\treturn branchName.startsWith('refs/heads/') ? branchName.replace('refs/heads/', '') : branchName;\n}\n\nfunction fromAzureUserToMember(user: AzureUser, type: 'issue'): IssueMember;\nfunction fromAzureUserToMember(user: AzureUser, type: 'pullRequest'): PullRequestMember;\nfunction fromAzureUserToMember(user: AzureUser, _type: 'issue' | 'pullRequest'): PullRequestMember | IssueMember {\n\treturn {\n\t\tavatarUrl: user.imageUrl,\n\t\tid: user.id,\n\t\tname: user.displayName,\n\t\tusername: user.uniqueName,\n\t\turl: user.url,\n\t};\n}\n\n/**\n * The URLs of a cross-repository pull request's fork, resolved by a lookup because the pull request payload embeds\n * only an abbreviated reference to it: `url` is the URL the head ref reports — the fork's web URL, or its clone URL\n * when the response carries no web URL — and `cloneHttps` its HTTPS clone URL.\n *\n * A fork that resolves neither is reported as no fork at all rather than as an object with nothing in it, so `url`\n * is always present here; `cloneHttps` alone is best-effort, and is absent when the response omits it or its URL\n * sits outside the integration's collection.\n */\nexport interface AzureForkRepositoryUrls {\n\turl: string;\n\tcloneHttps: string | undefined;\n}\n\n/**\n * `baseUrl` and `owner` are the prefix the pull request was read through; every URL the model reports is built from\n * them. The required `forkRepositoryUrls` argument makes each call site resolve the best-effort fork URLs explicitly.\n */\nexport function fromAzurePullRequest(\n\tpr: AzurePullRequest,\n\tprovider: Provider,\n\towner: string,\n\tbaseUrl: string,\n\tforkRepositoryUrls: AzureForkRepositoryUrls | undefined,\n\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): Issue {\n\tconst state = workItem.fields['System.State'];\n\treturn new Issue(\n\t\tprovider,\n\t\tworkItem.id.toString(),\n\t\tworkItem.id.toString(),\n\t\tworkItem.fields['System.Title'],\n\t\tworkItem._links.html.href,\n\t\tnew Date(workItem.fields['System.CreatedDate']),\n\t\tnew Date(workItem.fields['System.ChangedDate']),\n\t\tisClosedAzureWorkItemStateCategory(stateCategory),\n\t\tazureWorkItemsStateCategoryToState(stateCategory),\n\t\tfromAzureUserToMember(workItem.fields['System.CreatedBy'], 'issue'),\n\t\tworkItem.fields['System.AssignedTo'] != null\n\t\t\t? [fromAzureUserToMember(workItem.fields['System.AssignedTo'], 'issue')]\n\t\t\t: [],\n\t\tundefined,\n\t\tworkItem.fields['Microsoft.VSTS.Common.ClosedDate']\n\t\t\t? new Date(workItem.fields['Microsoft.VSTS.Common.ClosedDate'])\n\t\t\t: undefined,\n\t\tundefined,\n\t\tworkItem.fields['System.CommentCount'],\n\t\tundefined,\n\t\tworkItem.fields['System.Description'],\n\t\tproject,\n\t\tundefined,\n\t\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);\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\tconst description = fields['System.Description'];\n\treturn {\n\t\tid: id.toString(),\n\t\tnumber: id.toString(),\n\t\ttitle: title,\n\t\tcommentCount: typeof commentCount === 'number' && Number.isFinite(commentCount) ? commentCount : null,\n\t\tauthor: fromAzureWorkItemIdentity(fields['System.CreatedBy']),\n\t\tclosedDate: parseAzureWorkItemDate(fields['Microsoft.VSTS.Common.ClosedDate']),\n\t\tcreatedDate: createdDate,\n\t\tupdatedDate: parseAzureWorkItemDate(fields['System.ChangedDate']),\n\t\turl: typeof url === 'string' ? url : null,\n\t\tassignees: assignee != null ? [assignee] : [],\n\t\tdescription: typeof description === 'string' ? description : null,\n\t\tstate: typeof state === 'string' && state ? { name: state, color: null } : null,\n\t\ttype: typeof type === 'string' ? type : null,\n\t\titeration: toAzureWorkItemIteration(fields['System.IterationPath']),\n\t\trepository: null,\n\t\tproject: { namespace: namespace, name: project, resourceId: null, key: null, id: null },\n\t\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/** An identity field, as provider-apis maps it: `name` from `uniqueName`, `username` from `displayName`. */\nfunction fromAzureWorkItemIdentity(value: unknown): ProviderAccount | null {\n\tconst identity = value as\n\t\t| { id?: unknown; uniqueName?: unknown; displayName?: unknown; _links?: { avatar?: { href?: unknown } } }\n\t\t| null\n\t\t| undefined;\n\tif (typeof identity?.id !== 'string' || !identity.id.trim()) return null;\n\n\tconst avatarUrl = identity._links?.avatar?.href;\n\treturn {\n\t\tavatarUrl: typeof avatarUrl === 'string' ? avatarUrl : null,\n\t\temail: null,\n\t\tid: identity.id,\n\t\tname: typeof identity.uniqueName === 'string' ? identity.uniqueName : null,\n\t\tusername: typeof identity.displayName === 'string' ? identity.displayName : null,\n\t\turl: null,\n\t};\n}\n\n/** Only a UTC ISO timestamp naming a real calendar date and time, as provider-apis accepts; anything else is `null`. */\nfunction parseAzureWorkItemDate(value: unknown): Date | null {\n\tif (typeof value !== 'string') return null;\n\n\tconst match = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,7})?Z$/.exec(value);\n\tif (match == null) return null;\n\n\tconst [year, month, day, hour, minute, second] = match.slice(1).map(Number);\n\tconst isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n\tconst daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\tif (month < 1 || month > 12 || day < 1 || day > daysInMonth[month - 1] || hour > 23 || minute > 59 || second > 59) {\n\t\treturn null;\n\t}\n\n\tconst date = new Date(value);\n\treturn Number.isNaN(date.getTime()) ? null : date;\n}\n\n/** The SDK's `iteration`, by the same path rule as {@link toWorkItemIterations}. */\nfunction toAzureWorkItemIteration(path: unknown): ProviderIssue['iteration'] {\n\tconst [iteration] = toWorkItemIterations(typeof path === 'string' ? path : undefined) ?? [];\n\treturn iteration != null ? { path: iteration.id, name: iteration.name } : undefined;\n}\n\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}) when it has a closed date, parsed by the same rule, or a\n * terminal workflow `category`, which the caller classifies from the work item's state as the full read does.\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\treturn {\n\t\tstate:\n\t\t\tparseAzureWorkItemDate(fields?.['Microsoft.VSTS.Common.ClosedDate']) != null ||\n\t\t\tisTerminalIssueStateCategory(category)\n\t\t\t\t? 'closed'\n\t\t\t\t: 'opened',\n\t\tupdatedDate: updatedDate,\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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"}
|
|
1
|
+
{"version":3,"file":"models.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/models.ts"],"sourcesContent":["import 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 { pullRequestRevision } from '../../models/integration.js';\nimport type { ProviderAccount, ProviderIssue } from '../models.js';\nimport {\n\tfromProviderPullRequestMergeableState,\n\tfromProviderPullRequestState,\n\tGitPullRequestMergeableState,\n\tGitPullRequestReviewState,\n\tGitPullRequestState,\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): IssueOrPullRequestState {\n\treturn isClosedAzureWorkItemStateCategory(category) ? 'closed' : 'opened';\n}\n\nexport interface AzureLink {\n\thref: string;\n}\n\nexport interface AzureUser {\n\tdisplayName: string;\n\turl: string;\n\t_links: {\n\t\tavatar: AzureLink;\n\t};\n\tid: string;\n\tuniqueName: string;\n\timageUrl: string;\n\tdescriptor?: string;\n}\n\nexport interface AzureUserWithVote extends AzureUser {\n\tisFlagged?: boolean;\n\thasDeclined?: boolean;\n\tisReapprove?: boolean;\n\tisRequired?: boolean;\n\tvote?: AzurePullRequestVote;\n}\n\nexport type AzurePullRequestVote =\n\t| 10 // approved\n\t| 5 // approved with suggestions\n\t| 0 // no vote\n\t| -5 // waiting for author\n\t| -10; // rejected\n\nexport interface AzureWorkItemCommentVersionRef {\n\tcommentId: number;\n\tcreatedInRevision: number;\n\tisDeleted: boolean;\n\ttext: string;\n\turl: string;\n\tversion: number;\n}\n\nexport interface AzureWorkItemRelation {\n\tattributes: {\n\t\t[key: string]: string;\n\t};\n\trelation: string;\n\turl: string;\n}\n\nexport interface WorkItem {\n\t_links: {\n\t\tfields: AzureLink;\n\t\thtml: AzureLink;\n\t\tself: AzureLink;\n\t\tworkItemComments: AzureLink;\n\t\tworkItemRevisions: AzureLink;\n\t\tworkItemType: AzureLink;\n\t\tworkItemUpdates: AzureLink;\n\t};\n\tfields: {\n\t\t//'System.AreaPath': string;\n\t\t'System.TeamProject': string;\n\t\t'System.IterationPath'?: string;\n\t\t'System.WorkItemType': string;\n\t\t'System.State': string;\n\t\t// 'System.Reason': string;\n\t\t'System.AssignedTo': AzureUser;\n\t\t'System.CreatedDate': string;\n\t\t'System.CreatedBy': AzureUser;\n\t\t'System.ChangedDate': string;\n\t\t'System.ChangedBy': AzureUser;\n\t\t'System.CommentCount': number;\n\t\t'System.Description': string;\n\t\t'System.Title': string;\n\t\t'Microsoft.VSTS.Common.ClosedDate': string;\n\t\t// 'Microsoft.VSTS.Common.StateChangeDate': string;\n\t\t// 'Microsoft.VSTS.Common.Priority': number;\n\t\t// 'Microsoft.VSTS.Common.Severity': string;\n\t\t// 'Microsoft.VSTS.Common.ValueArea': string;\n\t};\n\tid: number;\n\trev: number;\n\turl: string;\n\tcommentVersionRef?: AzureWorkItemCommentVersionRef;\n\trelations?: AzureWorkItemRelation[];\n}\n\nexport interface AzureWorkItemState {\n\tname: string;\n\tcolor: string;\n\tcategory: AzureWorkItemStateCategory;\n}\n\nexport type AzurePullRequestStatus = 'abandoned' | 'active' | 'completed' | 'notSet';\nexport function azurePullRequestStatusToState(status: AzurePullRequestStatus): IssueOrPullRequestState {\n\tswitch (status) {\n\t\tcase 'abandoned':\n\t\t\treturn 'closed';\n\t\tcase 'completed':\n\t\t\treturn 'merged';\n\t\tcase 'active':\n\t\tcase 'notSet':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\nexport function isClosedAzurePullRequestStatus(status: AzurePullRequestStatus): boolean {\n\treturn azurePullRequestStatusToState(status) !== 'opened';\n}\n\nexport type AzureProjectState = 'createPending' | 'deleted' | 'deleting' | 'new' | 'unchanged' | 'wellFormed';\nexport type AzureProjectVisibility = 'private' | 'public';\n\nexport interface AzureProject {\n\tid: string;\n\tname: string;\n\turl: string;\n\tstate: AzureProjectState;\n\trevision: number;\n\tvisibility: AzureProjectVisibility;\n\tlastUpdateTime: string;\n}\n\nexport interface AzureRepositoryReference {\n\tid: string;\n\tname: string;\n\turl: string;\n\tremoteUrl?: string;\n}\n\nexport interface AzureRepository extends AzureRepositoryReference {\n\tproject: AzureProject;\n\tsize: number;\n\tremoteUrl: string;\n\tsshUrl: string;\n\twebUrl: string;\n\tisDisabled: boolean;\n\tisInMaintenance: boolean;\n}\n\nexport interface AzurePullRequestRepository extends AzureRepositoryReference {\n\tproject: Pick<AzureProject, 'id' | 'name'>;\n}\n\n/** The URL fields of a repository response — all the fork lookup reads, and all an older `api-version` promises. */\nexport type AzureRepositoryUrls = Partial<Pick<AzureRepository, 'webUrl' | 'remoteUrl'>>;\n\n/** The `GET .../_apis/git/repositories/{repositoryId or name}` response, adding fork/default-branch fields to {@link AzureRepository}. */\nexport interface AzureRepositoryWithMetadata extends AzureRepository {\n\t/** Fully-qualified ref, e.g. `refs/heads/main`. */\n\tdefaultBranch?: string;\n\tisFork?: boolean;\n\tparentRepository?: {\n\t\tid: string;\n\t\tname?: string;\n\t\tproject?: { id: string; name: string };\n\t};\n}\n\nexport interface AzureGitUser {\n\tdate?: string;\n\temail?: string;\n\timageUrl?: string;\n\tname: string;\n}\n\nexport interface AzureGitCommitRef {\n\tcommitId: string;\n\turl: string;\n}\n\nexport interface AzureGitCommit {\n\t_links: {\n\t\tchanges: AzureLink;\n\t\trepository: AzureLink;\n\t\tself: AzureLink;\n\t\tweb: AzureLink;\n\t};\n\tauthor: AzureGitUser;\n\tcomment: string;\n\tcommentTruncated?: boolean;\n\tcommitId: string;\n\tcommitTooManyChanges?: boolean;\n\tcommitter: AzureGitUser;\n\tparents: string[];\n\tpush: {\n\t\tdate: string;\n\t\tpushedBy: AzureUser;\n\t\tpushId: number;\n\t};\n\tremoteUrl: string;\n\tstatuses?: AzureGitStatus[];\n\ttreeId: string;\n\turl: string;\n\tworkItems?: AzureResourceRef[];\n}\n\nexport interface AzureResourceRef {\n\tid: string;\n\turl: string;\n}\n\nexport interface AzurePullRequestCompletionOptions {\n\tautoCompleteIgnoreConflicts: number[];\n\tbypassPolicy: boolean;\n\tbypassReason: string;\n\tdeleteSourceBranch: boolean;\n\tmergeCommitMessage: string;\n\tmergeStrategy: 'noFastForward' | 'rebase' | 'rebaseMerge' | 'squash';\n\tsquashMerge: boolean;\n\ttransitionWorkItems: boolean;\n\ttriggeredByAutoComplete: boolean;\n}\n\nexport interface AzureGitStatus {\n\tcontext: {\n\t\tname: string;\n\t\tgenre: string;\n\t};\n\tcreatedBy: AzureUser;\n\tcreateDate: string;\n\tdescription: string;\n\tstate: 'error' | 'failed' | 'notApplicable' | 'notSet' | 'pending' | 'succeeded';\n\ttargetUrl: string;\n\tupdateDate: string;\n}\n\nexport interface AzureGitForkRef {\n\tcreator: AzureUser;\n\tisLocked: boolean;\n\tisLockedBy: AzureUser;\n\tname: string;\n\tobjectId: string;\n\tpeeledObjectId: string;\n\trepository: AzureRepositoryReference;\n\tstatuses: AzureGitStatus[];\n\turl: string;\n}\n\nexport interface AzureWebApiTagDefinition {\n\tactive: boolean;\n\tid: string;\n\tname: string;\n\turl: string;\n}\n\nexport interface AzureGitPullRequestMergeOptions {\n\tconflictAuthorshipCommits: boolean;\n\tdetectRenameFalsePositives: boolean;\n\tdisableRenames: boolean;\n}\n\nexport type AzurePullRequestAsyncStatus =\n\t| 'conflicts'\n\t| 'failure'\n\t| 'notSet'\n\t| 'queued'\n\t| 'rejectedByPolicy'\n\t| 'succeeded';\n\nexport interface AzurePullRequest {\n\trepository: AzurePullRequestRepository;\n\tpullRequestId: number;\n\tcodeReviewId: number;\n\tstatus: AzurePullRequestStatus;\n\tcreatedBy: AzureUser;\n\tcreationDate: string;\n\tclosedDate?: string;\n\tclosedBy?: AzureUser; // Can be missed even if closedDate is presented.\n\ttitle: string;\n\tdescription: string;\n\tforkSource?: AzureGitForkRef;\n\tsourceRefName: string;\n\ttargetRefName: string;\n\tisDraft: boolean;\n\tmergeId: string;\n\tmergeStatus?: AzurePullRequestAsyncStatus;\n\tlastMergeCommit?: AzureGitCommitRef;\n\tlastMergeSourceCommit: AzureGitCommitRef;\n\tlastMergeTargetCommit: AzureGitCommitRef;\n\treviewers: AzureUserWithVote[];\n\turl: string;\n\tsupportsIterations: boolean;\n}\n\nexport interface AzurePullRequestWithLinks extends AzurePullRequest {\n\t_links: {\n\t\tself: AzureLink;\n\t\trepository: AzureLink;\n\t\tworkItems: AzureLink;\n\t\tsourceBranch: AzureLink;\n\t\ttargetBranch: AzureLink;\n\t\tstatuses: AzureLink;\n\t\tsourceCommit: AzureLink;\n\t\ttargetCommit: AzureLink;\n\t\tcreatedBy: AzureLink;\n\t\titerations: AzureLink;\n\t};\n\tartifactId: string;\n\tautoCompleteSetBy?: AzureUser;\n\tcommits?: AzureGitCommitRef[];\n\tcompletionOptions?: AzurePullRequestCompletionOptions;\n\tcompletionQueueTime?: string;\n\thasMultipleMergeBases?: boolean;\n\tlabels?: AzureWebApiTagDefinition[];\n\tmergeFailureMessage?: string;\n\tmergeFailureType?: 'caseSensitive' | 'none' | 'objectTooLarge' | 'unknown';\n\tmergeOptions?: AzureGitPullRequestMergeOptions;\n\tremoteUrl?: string;\n\tworkItemRefs?: AzureResourceRef[];\n}\n\nexport function getAzureDevOpsOwner(url: URL): string {\n\treturn url.pathname.split('/')[1];\n}\nexport function isVsts(domain: string): boolean {\n\treturn domain.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): Issue {\n\tconst state = workItem.fields['System.State'];\n\treturn new Issue(\n\t\tprovider,\n\t\tworkItem.id.toString(),\n\t\tworkItem.id.toString(),\n\t\tworkItem.fields['System.Title'],\n\t\tworkItem._links.html.href,\n\t\tnew Date(workItem.fields['System.CreatedDate']),\n\t\tnew Date(workItem.fields['System.ChangedDate']),\n\t\tisClosedAzureWorkItemStateCategory(stateCategory),\n\t\tazureWorkItemsStateCategoryToState(stateCategory),\n\t\tfromAzureUserToMember(workItem.fields['System.CreatedBy'], 'issue'),\n\t\tworkItem.fields['System.AssignedTo'] != null\n\t\t\t? [fromAzureUserToMember(workItem.fields['System.AssignedTo'], 'issue')]\n\t\t\t: [],\n\t\tundefined,\n\t\tworkItem.fields['Microsoft.VSTS.Common.ClosedDate']\n\t\t\t? new Date(workItem.fields['Microsoft.VSTS.Common.ClosedDate'])\n\t\t\t: undefined,\n\t\tundefined,\n\t\tworkItem.fields['System.CommentCount'],\n\t\tundefined,\n\t\tworkItem.fields['System.Description'],\n\t\tproject,\n\t\tundefined,\n\t\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);\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\tconst description = fields['System.Description'];\n\treturn {\n\t\tid: id.toString(),\n\t\tnumber: id.toString(),\n\t\ttitle: title,\n\t\tcommentCount: typeof commentCount === 'number' && Number.isFinite(commentCount) ? commentCount : null,\n\t\tauthor: fromAzureWorkItemIdentity(fields['System.CreatedBy']),\n\t\tclosedDate: parseAzureWorkItemDate(fields['Microsoft.VSTS.Common.ClosedDate']),\n\t\tcreatedDate: createdDate,\n\t\tupdatedDate: parseAzureWorkItemDate(fields['System.ChangedDate']),\n\t\turl: typeof url === 'string' ? url : null,\n\t\tassignees: assignee != null ? [assignee] : [],\n\t\tdescription: typeof description === 'string' ? description : null,\n\t\tstate: typeof state === 'string' && state ? { name: state, color: null } : null,\n\t\ttype: typeof type === 'string' ? type : null,\n\t\titeration: toAzureWorkItemIteration(fields['System.IterationPath']),\n\t\trepository: null,\n\t\tproject: { namespace: namespace, name: project, resourceId: null, key: null, id: null },\n\t\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/** An identity field, as provider-apis maps it: `name` from `uniqueName`, `username` from `displayName`. */\nfunction fromAzureWorkItemIdentity(value: unknown): ProviderAccount | null {\n\tconst identity = value as\n\t\t| { id?: unknown; uniqueName?: unknown; displayName?: unknown; _links?: { avatar?: { href?: unknown } } }\n\t\t| null\n\t\t| undefined;\n\tif (typeof identity?.id !== 'string' || !identity.id.trim()) return null;\n\n\tconst avatarUrl = identity._links?.avatar?.href;\n\treturn {\n\t\tavatarUrl: typeof avatarUrl === 'string' ? avatarUrl : null,\n\t\temail: null,\n\t\tid: identity.id,\n\t\tname: typeof identity.uniqueName === 'string' ? identity.uniqueName : null,\n\t\tusername: typeof identity.displayName === 'string' ? identity.displayName : null,\n\t\turl: null,\n\t};\n}\n\n/** Only a UTC ISO timestamp naming a real calendar date and time, as provider-apis accepts; anything else is `null`. */\nfunction parseAzureWorkItemDate(value: unknown): Date | null {\n\tif (typeof value !== 'string') return null;\n\n\tconst match = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,7})?Z$/.exec(value);\n\tif (match == null) return null;\n\n\tconst [year, month, day, hour, minute, second] = match.slice(1).map(Number);\n\tconst isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n\tconst daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\tif (month < 1 || month > 12 || day < 1 || day > daysInMonth[month - 1] || hour > 23 || minute > 59 || second > 59) {\n\t\treturn null;\n\t}\n\n\tconst date = new Date(value);\n\treturn Number.isNaN(date.getTime()) ? null : date;\n}\n\n/** The SDK's `iteration`, by the same path rule as {@link toWorkItemIterations}. */\nfunction toAzureWorkItemIteration(path: unknown): ProviderIssue['iteration'] {\n\tconst [iteration] = toWorkItemIterations(typeof path === 'string' ? path : undefined) ?? [];\n\treturn iteration != null ? { path: iteration.id, name: iteration.name } : undefined;\n}\n\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 closed =\n\t\tcategory != null\n\t\t\t? isTerminalIssueStateCategory(category)\n\t\t\t: parseAzureWorkItemDate(fields?.['Microsoft.VSTS.Common.ClosedDate']) != null;\n\treturn {\n\t\tstate: closed ? 'closed' : 'opened',\n\t\tupdatedDate: updatedDate,\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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"}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AzureWorkItemTypeStates } from "@gitkraken/provider-apis";
|
|
2
2
|
//#region ../plus/integrations/src/providers/azure/workItemStateCategories.d.ts
|
|
3
|
-
|
|
4
|
-
export type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
type WorkItemTypeStates = NonNullable<AzureWorkItemTypeStates['types'][number]['states']>;
|
|
4
|
+
export type AzureWorkItemStatesScope = {
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
namespace: string;
|
|
7
|
+
project: string;
|
|
8
|
+
token: string;
|
|
9
|
+
isPAT: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string): string;
|
|
10
12
|
export declare function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string): string;
|
|
11
|
-
/** {@link getAzureWorkItemTypeStatesPath} on `baseUrl`. */
|
|
12
13
|
export declare function getAzureWorkItemTypeStatesUrl(baseUrl: string, namespace: string, project: string, type: string): string;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
export declare class AzureWorkItemStatesCache {
|
|
15
|
+
private readonly types;
|
|
16
|
+
private readonly states;
|
|
17
|
+
clear(): void;
|
|
18
|
+
getTypes(scope: AzureWorkItemStatesScope, read: () => Promise<unknown>): Promise<string[]>;
|
|
19
|
+
getStates(scope: AzureWorkItemStatesScope, type: string, read: () => Promise<unknown>): Promise<WorkItemTypeStates>;
|
|
20
|
+
private read;
|
|
21
|
+
}
|
|
18
22
|
//#endregion
|
|
19
23
|
//# sourceMappingURL=workItemStateCategories.d.ts.map
|
|
@@ -1,32 +1,84 @@
|
|
|
1
|
+
import { PromiseCache } from "../../../../utils/promiseCache.js";
|
|
1
2
|
import { azureIssueStateCategory, encodeAzurePathSegment } from "./models.js";
|
|
2
3
|
//#region ../plus/integrations/src/providers/azure/workItemStateCategories.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* only as a preview; Azure DevOps Services accepts it too.
|
|
7
|
-
*/
|
|
4
|
+
function getAzureWorkItemTypesUrl(baseUrl, namespace, project) {
|
|
5
|
+
return `${baseUrl.replace(/\/$/, "")}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=6.0`;
|
|
6
|
+
}
|
|
8
7
|
function getAzureWorkItemTypeStatesPath(namespace, project, type) {
|
|
9
8
|
return `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=6.0-preview.1`;
|
|
10
9
|
}
|
|
11
|
-
/** {@link getAzureWorkItemTypeStatesPath} on `baseUrl`. */
|
|
12
10
|
function getAzureWorkItemTypeStatesUrl(baseUrl, namespace, project, type) {
|
|
13
11
|
return `${baseUrl.replace(/\/$/, "")}/${getAzureWorkItemTypeStatesPath(namespace, project, type)}`;
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
function parseWorkItemTypes(body) {
|
|
14
|
+
const types = body?.value;
|
|
15
|
+
if (!Array.isArray(types)) throw new Error("Azure DevOps returned no work item types");
|
|
16
|
+
const names = [];
|
|
17
|
+
for (const type of types) {
|
|
18
|
+
if (typeof type?.name !== "string" || !type.name.trim()) throw new Error("Azure DevOps returned an invalid work item type");
|
|
19
|
+
names.push(type.name);
|
|
20
|
+
}
|
|
21
|
+
return [...new Set(names)];
|
|
22
|
+
}
|
|
23
|
+
function parseWorkItemStates(body) {
|
|
20
24
|
const states = body?.value;
|
|
21
|
-
if (!Array.isArray(states)) throw new Error("Azure DevOps returned no work item states");
|
|
22
|
-
const
|
|
25
|
+
if (!Array.isArray(states) || states.length === 0) throw new Error("Azure DevOps returned no work item states");
|
|
26
|
+
const parsed = [];
|
|
23
27
|
for (const state of states) {
|
|
24
|
-
if (typeof state?.name !== "string")
|
|
25
|
-
|
|
28
|
+
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
|
+
parsed.push({
|
|
30
|
+
name: state.name,
|
|
31
|
+
category: state.category
|
|
32
|
+
});
|
|
26
33
|
}
|
|
27
|
-
return
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
function scopeKey(scope, type) {
|
|
37
|
+
return JSON.stringify([
|
|
38
|
+
scope.baseUrl.replace(/\/$/, ""),
|
|
39
|
+
scope.namespace.toLowerCase(),
|
|
40
|
+
scope.project.toLowerCase(),
|
|
41
|
+
type?.toLowerCase(),
|
|
42
|
+
scope.token,
|
|
43
|
+
scope.isPAT
|
|
44
|
+
]);
|
|
28
45
|
}
|
|
46
|
+
var AzureWorkItemStatesCache = class {
|
|
47
|
+
types = new PromiseCache({
|
|
48
|
+
createTTL: 3e5,
|
|
49
|
+
capacity: 256
|
|
50
|
+
});
|
|
51
|
+
states = new PromiseCache({
|
|
52
|
+
createTTL: 3e5,
|
|
53
|
+
capacity: 256
|
|
54
|
+
});
|
|
55
|
+
clear() {
|
|
56
|
+
this.types.clear();
|
|
57
|
+
this.states.clear();
|
|
58
|
+
}
|
|
59
|
+
getTypes(scope, read) {
|
|
60
|
+
return this.read(this.types, scopeKey(scope), async () => parseWorkItemTypes(await read()));
|
|
61
|
+
}
|
|
62
|
+
getStates(scope, type, read) {
|
|
63
|
+
return this.read(this.states, scopeKey(scope, type), async () => parseWorkItemStates(await read()));
|
|
64
|
+
}
|
|
65
|
+
async read(cache, key, load) {
|
|
66
|
+
const result = await cache.getOrCreate(key, async () => ({
|
|
67
|
+
status: "fulfilled",
|
|
68
|
+
value: await load()
|
|
69
|
+
}), { onError: (error) => cache.get(key) == null ? void 0 : {
|
|
70
|
+
value: {
|
|
71
|
+
status: "rejected",
|
|
72
|
+
reason: error
|
|
73
|
+
},
|
|
74
|
+
createTTL: 3e4,
|
|
75
|
+
suppress: true
|
|
76
|
+
} });
|
|
77
|
+
if (result.status === "rejected") throw result.reason;
|
|
78
|
+
return result.value;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
29
81
|
//#endregion
|
|
30
|
-
export { getAzureWorkItemTypeStatesPath, getAzureWorkItemTypeStatesUrl,
|
|
82
|
+
export { AzureWorkItemStatesCache, getAzureWorkItemTypeStatesPath, getAzureWorkItemTypeStatesUrl, getAzureWorkItemTypesUrl };
|
|
31
83
|
|
|
32
84
|
//# sourceMappingURL=workItemStateCategories.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workItemStateCategories.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemStateCategories.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"workItemStateCategories.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemStateCategories.ts"],"sourcesContent":["import type { AzureWorkItemTypeStates } from '@gitkraken/provider-apis';\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\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 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\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\n\tclear(): void {\n\t\tthis.types.clear();\n\t\tthis.states.clear();\n\t}\n\n\tgetTypes(scope: AzureWorkItemStatesScope, read: () => Promise<unknown>): Promise<string[]> {\n\t\treturn this.read(this.types, scopeKey(scope), async () => parseWorkItemTypes(await read()));\n\t}\n\n\tgetStates(\n\t\tscope: AzureWorkItemStatesScope,\n\t\ttype: string,\n\t\tread: () => Promise<unknown>,\n\t): Promise<WorkItemTypeStates> {\n\t\treturn this.read(this.states, scopeKey(scope, type), async () => parseWorkItemStates(await read()));\n\t}\n\n\tprivate async read<T>(\n\t\tcache: PromiseCache<string, PromiseSettledResult<T>>,\n\t\tkey: string,\n\t\tload: () => Promise<T>,\n\t): Promise<T> {\n\t\tconst result = await cache.getOrCreate(key, async () => ({ status: 'fulfilled', value: await load() }), {\n\t\t\t// Every caller must still see the failure; caching undefined would turn later pages into successes.\n\t\t\tonError: error =>\n\t\t\t\tcache.get(key) == null\n\t\t\t\t\t? undefined\n\t\t\t\t\t: { value: { status: 'rejected', reason: error }, createTTL: 30 * 1000, suppress: true },\n\t\t});\n\t\tif (result.status === 'rejected') throw result.reason;\n\n\t\treturn result.value;\n\t}\n}\n"],"mappings":";;;AAeA,SAAgB,yBAAyB,SAAiB,WAAmB,SAAyB;CACrG,OAAO,GAAG,QAAQ,QAAQ,OAAO,EAAE,EAAE,GAAG,uBAAuB,SAAS,EAAE,GAAG,uBAAuB,OAAO,EAAE;AAC9G;AAGA,SAAgB,+BAA+B,WAAmB,SAAiB,MAAsB;CACxG,OAAO,GAAG,uBAAuB,SAAS,EAAE,GAAG,uBAAuB,OAAO,EAAE,2BAA2B,uBAAuB,IAAI,EAAE;AACxI;AAEA,SAAgB,8BACf,SACA,WACA,SACA,MACS;CACT,OAAO,GAAG,QAAQ,QAAQ,OAAO,EAAE,EAAE,GAAG,+BAA+B,WAAW,SAAS,IAAI;AAChG;AAEA,SAAS,mBAAmB,MAAyB;CACpD,MAAM,QAAS,MAAqC;CACpD,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,0CAA0C;CAErF,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,QAAQ,OAA+B;EACjD,IAAI,OAAO,MAAM,SAAS,YAAY,CAAC,KAAK,KAAK,KAAK,GACrD,MAAM,IAAI,MAAM,iDAAiD;EAGlE,MAAM,KAAK,KAAK,IAAI;CACrB;CACA,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC1B;AAEA,SAAS,oBAAoB,MAAmC;CAC/D,MAAM,SAAU,MAAqC;CACrD,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG,MAAM,IAAI,MAAM,2CAA2C;CAE9G,MAAM,SAA+C,CAAC;CACtD,KAAK,MAAM,SAAS,QAAoD;EACvE,IACC,OAAO,OAAO,SAAS,YACvB,CAAC,MAAM,KAAK,KAAK,KACjB,OAAO,MAAM,aAAa,YAC1B,wBAAwB,MAAM,QAAsC,KAAK,QACzE,OAAO,MAAK,MAAK,EAAE,SAAS,MAAM,QAAQ,EAAE,aAAa,MAAM,QAAQ,GAEvE,MAAM,IAAI,MAAM,kDAAkD;EAGnE,OAAO,KAAK;GAAE,MAAM,MAAM;GAAM,UAAU,MAAM;EAAS,CAAC;CAC3D;CACA,OAAO;AACR;AAEA,SAAS,SAAS,OAAiC,MAAuB;CACzE,OAAO,KAAK,UAAU;EACrB,MAAM,QAAQ,QAAQ,OAAO,EAAE;EAC/B,MAAM,UAAU,YAAY;EAC5B,MAAM,QAAQ,YAAY;EAC1B,MAAM,YAAY;EAClB,MAAM;EACN,MAAM;CACP,CAAC;AACF;AAEA,IAAa,2BAAb,MAAsC;CACrC,QAAyB,IAAI,aAAqD;EACjF,WAAW;EACX,UAAU;CACX,CAAC;CACD,SAA0B,IAAI,aAA+D;EAC5F,WAAW;EACX,UAAU;CACX,CAAC;CAED,QAAc;EACb,KAAK,MAAM,MAAM;EACjB,KAAK,OAAO,MAAM;CACnB;CAEA,SAAS,OAAiC,MAAiD;EAC1F,OAAO,KAAK,KAAK,KAAK,OAAO,SAAS,KAAK,GAAG,YAAY,mBAAmB,MAAM,KAAK,CAAC,CAAC;CAC3F;CAEA,UACC,OACA,MACA,MAC8B;EAC9B,OAAO,KAAK,KAAK,KAAK,QAAQ,SAAS,OAAO,IAAI,GAAG,YAAY,oBAAoB,MAAM,KAAK,CAAC,CAAC;CACnG;CAEA,MAAc,KACb,OACA,KACA,MACa;EACb,MAAM,SAAS,MAAM,MAAM,YAAY,KAAK,aAAa;GAAE,QAAQ;GAAa,OAAO,MAAM,KAAK;EAAE,IAAI,EAEvG,UAAS,UACR,MAAM,IAAI,GAAG,KAAK,OACf,KAAA,IACA;GAAE,OAAO;IAAE,QAAQ;IAAY,QAAQ;GAAM;GAAG,WAAW;GAAW,UAAU;EAAK,EAC1F,CAAC;EACD,IAAI,OAAO,WAAW,YAAY,MAAM,OAAO;EAE/C,OAAO,OAAO;CACf;AACD"}
|
|
@@ -15,6 +15,7 @@ import { AzureOrganizationDescriptor, AzureProjectDescriptor, AzureProjectInputD
|
|
|
15
15
|
import { AccountWideIssuesResult, SearchMyIssuesOptions } from "../models/issueReads.js";
|
|
16
16
|
import { GitHostIntegration, SearchMyPullRequestsOptions, SearchPullRequestsOptions } from "../models/gitHostIntegration.js";
|
|
17
17
|
import { IssueEtagFields, IssueEtagInclude, PullRequestEtagFields, PullRequestEtagInclude } from "../models/integration.js";
|
|
18
|
+
import { AzureDevOpsApi } from "./azure/azure.js";
|
|
18
19
|
import { IntegrationAuthenticationProviderDescriptor } from "../authentication/integrationAuthenticationProvider.js";
|
|
19
20
|
import { CollectionScopeFailure, GitPullRequestState } from "@gitkraken/provider-apis";
|
|
20
21
|
//#region ../plus/integrations/src/providers/azureDevOps.d.ts
|
|
@@ -24,6 +25,23 @@ export declare function getAzureRepositoryIdentity(repo: Pick<AzureRepositoryDes
|
|
|
24
25
|
repositoryName: string;
|
|
25
26
|
};
|
|
26
27
|
export declare function getAzureRepositoryApiBaseUrl(baseUrl: string, repo: Pick<AzureRepositoryDescriptor, 'owner' | 'virtualDirectory'>): string;
|
|
28
|
+
/**
|
|
29
|
+
* {@link getAzureRepositoryApiBaseUrl}, `undefined` when the connection doesn't serve `repo`'s virtual directory or it
|
|
30
|
+
* isn't a valid one.
|
|
31
|
+
*/
|
|
32
|
+
export declare function findAzureRepositoryApiBaseUrl(baseUrl: string, repo: Pick<AzureRepositoryDescriptor, 'owner' | 'virtualDirectory'>): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Whether `repo`'s path could be the project-less form of a repository named after its project, as read below the
|
|
35
|
+
* connection `baseUrl` addresses: that reading moves the parsed owner into the virtual directory, so its directory and
|
|
36
|
+
* owner must be one of {@link getAzureProjectlessDirectories}.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isAzureProjectlessCandidate(baseUrl: string, repo: Pick<AzureRepositoryDescriptor, 'owner' | 'name' | 'virtualDirectory'>): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The virtual directory ahead of `owner` (empty when nothing is) in whichever of {@link getAzureProjectlessDirectories}
|
|
41
|
+
* ends with it, `undefined` when none does. A caller-supplied repository scope carries only its owner, so this places
|
|
42
|
+
* it where {@link isAzureProjectlessCandidate} expects it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getAzureScopeVirtualDirectory(baseUrl: string, owner: string): string | undefined;
|
|
27
45
|
/**
|
|
28
46
|
* Whether two collection or project names name the same scope. Azure DevOps treats them case-insensitively, so a
|
|
29
47
|
* name typed by hand must still select the collection or project discovery reported.
|
|
@@ -31,6 +49,20 @@ export declare function getAzureRepositoryApiBaseUrl(baseUrl: string, repo: Pick
|
|
|
31
49
|
export declare function sameAzureName(a: string, b: string): boolean;
|
|
32
50
|
/** The distinct names in `names`, compared as Azure compares them, keeping the first spelling of each. */
|
|
33
51
|
export declare function uniqueAzureNames(names: readonly string[]): string[];
|
|
52
|
+
/** The repository a remote-derived descriptor names, and the base its requests append it to. */
|
|
53
|
+
export interface AzureRepositoryTarget<T extends Pick<AzureRepositoryDescriptor, 'owner' | 'name' | 'project' | 'virtualDirectory'>> {
|
|
54
|
+
repo: T;
|
|
55
|
+
baseUrl: string;
|
|
56
|
+
/** The path may name its collection where the repository was read, which couldn't be told apart. */
|
|
57
|
+
unsettled?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Re-reads a remote's `{owner}/{project}/_git/{repo}` as the project-less form of a repository named after its
|
|
61
|
+
* project: behind a path, `{vdir}/{collection}/_git/{repo}` parses as collection `{vdir}` and project `{collection}`.
|
|
62
|
+
* When `collections` lists the parsed project but not the parsed owner, the owner joins the virtual directory, the
|
|
63
|
+
* parsed project is the collection, and the project is the repository's own name. Otherwise returns `repo`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveAzureProjectlessRepository<T extends Pick<AzureRepositoryDescriptor, 'owner' | 'name' | 'project' | 'virtualDirectory'>>(repo: T, collections: readonly string[]): T;
|
|
34
66
|
/**
|
|
35
67
|
* Flags the reviews of `pr` that name one of the current user's groups (see `PullRequestReviewer.isMyGroup`), which a
|
|
36
68
|
* reviewer read finds through that group rather than through the user.
|
|
@@ -75,6 +107,18 @@ export declare abstract class AzureDevOpsIntegrationBase<TIntegrationId extends
|
|
|
75
107
|
};
|
|
76
108
|
/** The base a request or link below `collection` appends the collection to; see {@link getCollectionApiOptions}. */
|
|
77
109
|
protected collectionApiBaseUrl(session: ProviderAuthenticationSession, _collection: string): string;
|
|
110
|
+
/**
|
|
111
|
+
* The repository a remote-derived descriptor names, and the base its requests append it to. Azure DevOps Services
|
|
112
|
+
* reads the descriptor as parsed; the server override first settles a path the parser can't (see
|
|
113
|
+
* {@link resolveAzureProjectlessRepository}).
|
|
114
|
+
*/
|
|
115
|
+
protected getRepositoryTarget<T extends Pick<AzureRepositoryDescriptor, 'owner' | 'name' | 'project' | 'virtualDirectory'>>(session: ProviderAuthenticationSession, repo: T, _cancellation?: AbortSignal): Promise<AzureRepositoryTarget<T>>;
|
|
116
|
+
/**
|
|
117
|
+
* Reads `repo` with GitLens' Azure client at its target (see {@link getRepositoryTarget}). A read that finds nothing
|
|
118
|
+
* for a target whose path couldn't be settled may have addressed the wrong collection, so it throws instead, which
|
|
119
|
+
* keeps that miss out of the host's caches until the path can be settled.
|
|
120
|
+
*/
|
|
121
|
+
protected readRepository<R>(session: ProviderAuthenticationSession, repo: AzureRepositoryDescriptor, read: (azure: AzureDevOpsApi, target: AzureRepositoryTarget<AzureRepositoryDescriptor>) => Promise<R | undefined>, cancellation?: AbortSignal): Promise<R | undefined>;
|
|
78
122
|
/** Reads the organizations (Azure DevOps Server: the collections) the account can see. */
|
|
79
123
|
protected requestResourcesForUser(session: ProviderAuthenticationSession, userId: string): Promise<ProviderAzureResource[] | undefined>;
|
|
80
124
|
/**
|