@gitkraken/core-gitlens 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/CHANGELOG.md +28 -2
  2. package/dist/git/remotes/azure-devops.js +3 -3
  3. package/dist/git/remotes/azure-devops.js.map +1 -1
  4. package/dist/plus/integrations/authentication/configuredIntegrationService.js +9 -2
  5. package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
  6. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +1 -1
  7. package/dist/plus/integrations/collectionMetadata.d.ts +48 -3
  8. package/dist/plus/integrations/collectionMetadata.js +138 -8
  9. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  10. package/dist/plus/integrations/errors.d.ts +3 -1
  11. package/dist/plus/integrations/errors.js +7 -3
  12. package/dist/plus/integrations/errors.js.map +1 -1
  13. package/dist/plus/integrations/index.d.ts +3 -3
  14. package/dist/plus/integrations/index.js.map +1 -1
  15. package/dist/plus/integrations/integrationService.d.ts +35 -1
  16. package/dist/plus/integrations/integrationService.js +90 -43
  17. package/dist/plus/integrations/integrationService.js.map +1 -1
  18. package/dist/plus/integrations/lite.js +5 -4
  19. package/dist/plus/integrations/lite.js.map +1 -1
  20. package/dist/plus/integrations/manager.d.ts +68 -23
  21. package/dist/plus/integrations/models/gitHostIntegration.d.ts +31 -15
  22. package/dist/plus/integrations/models/gitHostIntegration.js +71 -22
  23. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  24. package/dist/plus/integrations/models/integration.d.ts +70 -12
  25. package/dist/plus/integrations/models/integration.js +56 -1
  26. package/dist/plus/integrations/models/integration.js.map +1 -1
  27. package/dist/plus/integrations/models/issueReads.d.ts +17 -0
  28. package/dist/plus/integrations/models/issuesIntegration.js +2 -0
  29. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  30. package/dist/plus/integrations/models/pullRequestReads.d.ts +26 -0
  31. package/dist/plus/integrations/providers/azure/azure.d.ts +66 -1
  32. package/dist/plus/integrations/providers/azure/azure.js +192 -12
  33. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  34. package/dist/plus/integrations/providers/azure/search.d.ts +144 -0
  35. package/dist/plus/integrations/providers/azure/search.js +250 -0
  36. package/dist/plus/integrations/providers/azure/search.js.map +1 -0
  37. package/dist/plus/integrations/providers/azureDevOps.d.ts +73 -36
  38. package/dist/plus/integrations/providers/azureDevOps.js +191 -122
  39. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  40. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +176 -0
  41. package/dist/plus/integrations/providers/azureDevOpsServer.js +560 -0
  42. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -0
  43. package/dist/plus/integrations/providers/bitbucket-server/discovery.d.ts +12 -0
  44. package/dist/plus/integrations/providers/bitbucket-server/discovery.js +128 -0
  45. package/dist/plus/integrations/providers/bitbucket-server/discovery.js.map +1 -0
  46. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +10 -1
  47. package/dist/plus/integrations/providers/bitbucket-server/models.js +3 -2
  48. package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
  49. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +58 -0
  50. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +485 -0
  51. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -0
  52. package/dist/plus/integrations/providers/bitbucket-server.d.ts +61 -3
  53. package/dist/plus/integrations/providers/bitbucket-server.js +114 -20
  54. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  55. package/dist/plus/integrations/providers/bitbucket.d.ts +9 -1
  56. package/dist/plus/integrations/providers/bitbucket.js +13 -2
  57. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  58. package/dist/plus/integrations/providers/github.d.ts +6 -6
  59. package/dist/plus/integrations/providers/github.js +35 -34
  60. package/dist/plus/integrations/providers/github.js.map +1 -1
  61. package/dist/plus/integrations/providers/gitlab.d.ts +6 -4
  62. package/dist/plus/integrations/providers/gitlab.js +32 -28
  63. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  64. package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
  65. package/dist/plus/integrations/providers/jira-server.d.ts +9 -0
  66. package/dist/plus/integrations/providers/jira-server.js +15 -7
  67. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  68. package/dist/plus/integrations/providers/jira.d.ts +9 -0
  69. package/dist/plus/integrations/providers/jira.js +14 -2
  70. package/dist/plus/integrations/providers/jira.js.map +1 -1
  71. package/dist/plus/integrations/providers/linear.js +2 -2
  72. package/dist/plus/integrations/providers/models.d.ts +10 -1
  73. package/dist/plus/integrations/providers/models.js +86 -0
  74. package/dist/plus/integrations/providers/models.js.map +1 -1
  75. package/dist/plus/integrations/providers/providersApi.d.ts +10 -0
  76. package/dist/plus/integrations/providers/providersApi.js +18 -3
  77. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  78. package/dist/plus/integrations/providers/utils.d.ts +12 -1
  79. package/dist/plus/integrations/providers/utils.js +36 -6
  80. package/dist/plus/integrations/providers/utils.js.map +1 -1
  81. package/dist/plus/integrations/reads/broaden.js +6 -6
  82. package/dist/plus/integrations/reads/broaden.js.map +1 -1
  83. package/dist/plus/integrations/reads/context.d.ts +1 -1
  84. package/dist/plus/integrations/reads/counts.d.ts +18 -4
  85. package/dist/plus/integrations/reads/counts.js +66 -15
  86. package/dist/plus/integrations/reads/counts.js.map +1 -1
  87. package/dist/plus/integrations/reads/cursors.js +1 -1
  88. package/dist/plus/integrations/reads/drains.d.ts +2 -1
  89. package/dist/plus/integrations/reads/drains.js +1 -1
  90. package/dist/plus/integrations/reads/filters.d.ts +1 -1
  91. package/dist/plus/integrations/reads/filters.js +17 -12
  92. package/dist/plus/integrations/reads/filters.js.map +1 -1
  93. package/dist/plus/integrations/reads/hierarchy.js +2 -2
  94. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  95. package/dist/plus/integrations/reads/hierarchy.utils.js +1 -1
  96. package/dist/plus/integrations/reads/issueBatch.js +1 -1
  97. package/dist/plus/integrations/reads/issueTracker.js +2 -2
  98. package/dist/plus/integrations/reads/issues.js +2 -2
  99. package/dist/plus/integrations/reads/paging.js +1 -1
  100. package/dist/plus/integrations/reads/resolveRepository.d.ts +12 -11
  101. package/dist/plus/integrations/reads/resolveRepository.js +110 -19
  102. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
  103. package/dist/plus/integrations/reads/searchIssues.js +4 -2
  104. package/dist/plus/integrations/reads/searchIssues.js.map +1 -1
  105. package/dist/plus/integrations/reads/searchPullRequests.js +9 -3
  106. package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
  107. package/dist/plus/integrations/reads/sweeps.js +1 -1
  108. package/dist/plus/integrations/reads/trackerIssue.d.ts +9 -5
  109. package/dist/plus/integrations/reads/trackerIssue.js +15 -9
  110. package/dist/plus/integrations/reads/trackerIssue.js.map +1 -1
  111. package/dist/plus/integrations/reads/warnings.d.ts +6 -2
  112. package/dist/plus/integrations/reads/warnings.js +15 -19
  113. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  114. package/dist/plus/integrations/results.d.ts +84 -6
  115. package/dist/plus/integrations/results.js +9 -2
  116. package/dist/plus/integrations/results.js.map +1 -1
  117. package/dist/plus/integrations/utils/domain.utils.d.ts +19 -0
  118. package/dist/plus/integrations/utils/domain.utils.js +36 -1
  119. package/dist/plus/integrations/utils/domain.utils.js.map +1 -1
  120. package/dist/plus/integrations/utils/integration.utils.d.ts +1 -0
  121. package/dist/plus/integrations/utils/integration.utils.js +12 -1
  122. package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
  123. package/docs/integrations.md +263 -74
  124. package/docs/kepler-read-api-parity.md +19 -6
  125. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"jira.js","names":[],"sources":["../../../../../plus/integrations/src/providers/jira.ts"],"sourcesContent":["import type { CollectionMetadata } from '@gitkraken/provider-apis';\nimport * as l10n from '@vscode/l10n';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '@gitlens/git/models/autolink.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { IssueResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { throwIfCallerContractError, toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { IssuesCloudHostIntegrationId } from '../constants.js';\nimport type { IssuesForProjectOptions, ProjectIssuesDrain } from '../models/issueReads.js';\nimport { IssuesIntegration } from '../models/issuesIntegration.js';\nimport type { ProviderApiCollectionResult, ProviderIssue } from './models.js';\nimport { IssueFilter, providersMetadata, toAccount, toIssueShape } from './models.js';\nimport { collectProviderPagedResult, mergeCollectionMetadata } from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[IssuesCloudHostIntegrationId.Jira];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\nconst maxPagesPerRequest = 10;\n\ntype JiraProjectIssuesDrain<T> = {\n\tissues: T[];\n\tstatus: 'complete' | 'backstop' | 'incomplete';\n\tmetadata?: CollectionMetadata;\n};\n\nexport type JiraBaseDescriptor = IssueResourceDescriptor;\n\nexport interface JiraOrganizationDescriptor extends JiraBaseDescriptor {\n\turl: string;\n\tavatarUrl: string;\n}\n\nexport interface JiraProjectDescriptor extends JiraBaseDescriptor {\n\tresourceId: string;\n}\n\nexport class JiraIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Jira> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\treadonly id = IssuesCloudHostIntegrationId.Jira;\n\tprotected readonly key = this.id;\n\treadonly name: string = 'Jira';\n\n\tget domain(): string {\n\t\treturn metadata.domain;\n\t}\n\n\tprotected get apiBaseUrl(): string {\n\t\treturn 'https://api.atlassian.com';\n\t}\n\n\tprivate _autolinks: Map<string, (AutolinkReference | DynamicAutolinkReference)[]> | undefined;\n\toverride async autolinks(): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected || this._session == null || this._organizations == null || this._projects == null) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst cachedAutolinks = this._autolinks?.get(this._session.accessToken);\n\t\tif (cachedAutolinks != null) return cachedAutolinks;\n\n\t\tconst autolinks: (AutolinkReference | DynamicAutolinkReference)[] = [];\n\t\tconst organizations = this._organizations.get(this._session.accessToken);\n\t\tif (organizations != null) {\n\t\t\tfor (const organization of organizations) {\n\t\t\t\tconst projects = this._projects.get(`${this._session.accessToken}:${organization.id}`);\n\t\t\t\tif (projects != null) {\n\t\t\t\t\tfor (const project of projects) {\n\t\t\t\t\t\tconst dashedPrefix = `${project.key}-`;\n\t\t\t\t\t\tconst underscoredPrefix = `${project.key}_`;\n\t\t\t\t\t\tautolinks.push({\n\t\t\t\t\t\t\tprefix: dashedPrefix,\n\t\t\t\t\t\t\turl: `${organization.url}/browse/${dashedPrefix}<num>`,\n\t\t\t\t\t\t\talphanumeric: false,\n\t\t\t\t\t\t\tignoreCase: false,\n\t\t\t\t\t\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, organization.name),\n\n\t\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\t\tdescription: l10n.t('{0} Issue {1}', organization.name, `${dashedPrefix}<num>`),\n\t\t\t\t\t\t\tdescriptor: { ...organization },\n\t\t\t\t\t\t});\n\t\t\t\t\t\tautolinks.push({\n\t\t\t\t\t\t\tprefix: underscoredPrefix,\n\t\t\t\t\t\t\turl: `${organization.url}/browse/${dashedPrefix}<num>`,\n\t\t\t\t\t\t\talphanumeric: false,\n\t\t\t\t\t\t\tignoreCase: false,\n\t\t\t\t\t\t\treferenceType: 'branch',\n\t\t\t\t\t\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, organization.name),\n\n\t\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\t\tdescription: l10n.t('{0} Issue {1}', organization.name, `${dashedPrefix}<num>`),\n\t\t\t\t\t\t\tdescriptor: { ...organization },\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._autolinks ??= new Map<string, (AutolinkReference | DynamicAutolinkReference)[]>();\n\t\tthis._autolinks.set(this._session.accessToken, autolinks);\n\n\t\treturn autolinks;\n\t}\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: JiraOrganizationDescriptor,\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await api.getCurrentUserForResource(toTokenWithInfo(this.id, session), resource.id);\n\n\t\tif (user == null) return undefined;\n\t\treturn toAccount(user, this);\n\t}\n\n\tprivate _organizations: Map<string, JiraOrganizationDescriptor[] | undefined> | undefined;\n\tprotected override async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<JiraOrganizationDescriptor[] | undefined> {\n\t\tconst { accessToken } = session;\n\t\tthis._organizations ??= new Map<string, JiraOrganizationDescriptor[] | undefined>();\n\n\t\tconst cachedResources = this._organizations.get(accessToken);\n\n\t\tif (cachedResources == null || force) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst resources = await api.getJiraResourcesForCurrentUser(toTokenWithInfo(this.id, session));\n\t\t\tthis._organizations.set(\n\t\t\t\taccessToken,\n\t\t\t\tresources != null ? resources.map(r => ({ ...r, key: r.id })) : undefined,\n\t\t\t);\n\t\t}\n\n\t\treturn this._organizations.get(accessToken);\n\t}\n\n\tprivate _projects: Map<string, JiraProjectDescriptor[] | undefined> | undefined;\n\tprotected override async getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: JiraOrganizationDescriptor[],\n\t\tforce: boolean = false,\n\t): Promise<JiraProjectDescriptor[] | undefined> {\n\t\treturn (await this.getProviderProjectsForResourcesWithMetadata(session, resources, force)).values;\n\t}\n\n\tprotected override async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: JiraOrganizationDescriptor[],\n\t\tforce: boolean = false,\n\t): Promise<ProviderApiCollectionResult<JiraProjectDescriptor>> {\n\t\tconst { accessToken } = session;\n\t\tconst projectsCache = (this._projects ??= new Map<string, JiraProjectDescriptor[] | undefined>());\n\n\t\tlet resourcesWithoutProjects = [];\n\t\tif (force) {\n\t\t\tresourcesWithoutProjects = resources;\n\t\t} else {\n\t\t\tfor (const resource of resources) {\n\t\t\t\tconst resourceKey = `${accessToken}:${resource.id}`;\n\t\t\t\tconst cachedProjects = projectsCache.get(resourceKey);\n\t\t\t\tif (cachedProjects == null) {\n\t\t\t\t\tresourcesWithoutProjects.push(resource);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet metadata: CollectionMetadata | undefined;\n\t\tconst partialProjects: JiraProjectDescriptor[] = [];\n\t\tif (resourcesWithoutProjects.length > 0) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t\tconst drains = await Promise.allSettled(\n\t\t\t\tresourcesWithoutProjects.map(async resource => ({\n\t\t\t\t\tresource: resource,\n\t\t\t\t\tresult: await collectProviderPagedResult(\n\t\t\t\t\t\tcursor => api.getJiraProjectsForResource(tokenWithInfo, resource.id, { cursor: cursor }),\n\t\t\t\t\t\tmaxPagesPerRequest,\n\t\t\t\t\t\t{ providerId: this.id, resourceId: resource.id },\n\t\t\t\t\t),\n\t\t\t\t})),\n\t\t\t);\n\n\t\t\tdrains.forEach((drain, index) => {\n\t\t\t\tif (drain.status === 'rejected') {\n\t\t\t\t\tconst resource = resourcesWithoutProjects[index];\n\t\t\t\t\tif (resource == null) return;\n\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure({ providerId: this.id, resourceId: resource.id }, drain.reason),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst { resource, result } = drain.value;\n\t\t\t\tmetadata = mergeCollectionMetadata(metadata, result.metadata);\n\t\t\t\tconst projects = result.values\n\t\t\t\t\t.filter(project => project.resourceId === resource.id)\n\t\t\t\t\t.map(project => ({ ...project }));\n\t\t\t\tconst incomplete =\n\t\t\t\t\tresult.truncated === true ||\n\t\t\t\t\t(result.metadata != null && result.metadata.completeness !== 'complete');\n\t\t\t\tif (incomplete) {\n\t\t\t\t\tpartialProjects.push(...projects);\n\t\t\t\t\tif (\n\t\t\t\t\t\tresult.truncated === true &&\n\t\t\t\t\t\t(result.metadata == null || result.metadata.completeness === 'complete')\n\t\t\t\t\t) {\n\t\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, { completeness: 'partial' });\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tprojectsCache.set(`${accessToken}:${resource.id}`, projects);\n\t\t\t});\n\t\t}\n\n\t\tconst values = resources.reduce<JiraProjectDescriptor[]>((projects, resource) => {\n\t\t\tconst resourceProjects = projectsCache.get(`${accessToken}:${resource.id}`);\n\t\t\tif (resourceProjects != null) {\n\t\t\t\tprojects.push(...resourceProjects);\n\t\t\t}\n\t\t\treturn projects;\n\t\t}, partialProjects);\n\n\t\tconst projectsByIdentity = new Map<string, JiraProjectDescriptor>();\n\t\tfor (const project of values) {\n\t\t\tconst identity = `${project.resourceId}:${project.id}`;\n\t\t\tif (!projectsByIdentity.has(identity)) {\n\t\t\t\tprojectsByIdentity.set(identity, project);\n\t\t\t}\n\t\t}\n\t\treturn { values: [...projectsByIdentity.values()], metadata: metadata };\n\t}\n\n\tprotected override async getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: JiraProjectDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;\n\t}\n\n\tprotected override async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: JiraProjectDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\n\t\tconst api = await this.getProvidersApi();\n\n\t\tconst projectScope = {\n\t\t\tproviderId: this.id,\n\t\t\tresourceId: project.resourceId,\n\t\t\tprojectId: project.name,\n\t\t};\n\t\tconst drainIssues = async (scope: {\n\t\t\tauthorLogin?: string;\n\t\t\tassigneeLogins?: string[];\n\t\t\tmentionLogin?: string;\n\t\t}): Promise<JiraProjectIssuesDrain<ProviderIssue>> => {\n\t\t\tconst collected: ProviderIssue[] = [];\n\t\t\tlet cursor: string | undefined;\n\t\t\tlet status: JiraProjectIssuesDrain<ProviderIssue>['status'] = 'complete';\n\t\t\tlet metadata: CollectionMetadata | undefined;\n\t\t\tfor (let i = 0; i < maxPagesPerRequest; i++) {\n\t\t\t\tlet result: Awaited<ReturnType<typeof api.getIssuesForProjectPaged>> | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tresult = await api.getIssuesForProjectPaged(tokenWithInfo, project.name, project.resourceId, {\n\t\t\t\t\t\t...scope,\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tsort: options?.sort,\n\t\t\t\t\t});\n\t\t\t\t} catch (ex) {\n\t\t\t\t\t// A page failure after the first page leaves the already-drained prefix intact; record the\n\t\t\t\t\t// failure at the project scope instead of re-throwing and discarding the prefix. If nothing was\n\t\t\t\t\t// fetched yet, the original throw behavior is preserved so the caller sees a hard error rather\n\t\t\t\t\t// than an empty partial success.\n\t\t\t\t\tif (collected.length === 0) throw ex;\n\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [toCollectionScopeFailure(projectScope, ex)],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result == null) {\n\t\t\t\t\tif (cursor == null) break;\n\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure(\n\t\t\t\t\t\t\t\tprojectScope,\n\t\t\t\t\t\t\t\tnew Error('Jira returned no page after advertising a continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcollected.push(...result.data);\n\t\t\t\tif (!result.hasMore) break;\n\n\t\t\t\t// The provider claims more pages but gave no advancing cursor: we can't continue, so the drain\n\t\t\t\t// is incomplete — flag it rather than silently stopping (matches drainPullRequests/Repositories).\n\t\t\t\tif (result.nextCursor == null || result.nextCursor === cursor) {\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure(\n\t\t\t\t\t\t\t\tprojectScope,\n\t\t\t\t\t\t\t\tnew Error('Jira returned no advancing issue continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcursor = result.nextCursor;\n\t\t\t\tif (i === maxPagesPerRequest - 1) {\n\t\t\t\t\tstatus = 'backstop';\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tissues: collected,\n\t\t\t\tstatus: status,\n\t\t\t\tmetadata: metadata,\n\t\t\t};\n\t\t};\n\n\t\tconst getSearchedUserIssuesForFilter = async (\n\t\t\tuser: string,\n\t\t\tfilter: IssueFilter,\n\t\t): Promise<JiraProjectIssuesDrain<IssueShape>> => {\n\t\t\t// `assignee` and `creator` are user FIELDS: Jira resolves an accountId against the directory, which is\n\t\t\t// the identity that keeps matching once a display name cannot be looked up — a deactivated account, or\n\t\t\t// a profile whose visibility hides the name. Measured against a live site: a deactivated assignee\n\t\t\t// returns its issues by accountId and an empty page by display name, and Jira reports that miss as a\n\t\t\t// successful empty search rather than an error, so the list simply appears empty.\n\t\t\t//\n\t\t\t// `mention` is NOT a user field. It is `comment ~ \"...\"`, a free-text search over comment bodies, so an\n\t\t\t// accountId matches nothing there and the display name is the only value that can. Hence the id is\n\t\t\t// applied to the first two and the handle is kept for the third, rather than swapping `user` wholesale.\n\t\t\tconst userField = options?.userId ?? user;\n\t\t\tconst result = await drainIssues({\n\t\t\t\tauthorLogin: filter === IssueFilter.Author ? userField : undefined,\n\t\t\t\tassigneeLogins: filter === IssueFilter.Assignee ? [userField] : undefined,\n\t\t\t\tmentionLogin: filter === IssueFilter.Mention ? user : undefined,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tissues: result.issues\n\t\t\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t\t\t.filter((r): r is IssueShape => r !== undefined),\n\t\t\t\tstatus: result.status,\n\t\t\t\tmetadata: result.metadata,\n\t\t\t};\n\t\t};\n\n\t\tif (options?.user != null) {\n\t\t\tconst user = options.user;\n\t\t\t// A resolved user always scopes the read. Default to the assignee filter (\"my issues\") when no\n\t\t\t// explicit filters are given — otherwise a caller that scopes by user but omits filters would fall\n\t\t\t// through to the unscoped fetch below and get every issue in the project instead of the user's.\n\t\t\tconst filters = options.filters?.length ? options.filters : [IssueFilter.Assignee];\n\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\tfilters.map(filter => getSearchedUserIssuesForFilter(user, filter)),\n\t\t\t);\n\n\t\t\t// If every filter branch rejected, the read failed outright — propagate the first rejection instead\n\t\t\t// of returning an empty list, which the facade (getIssuesForProjectResult → runCaptured) would\n\t\t\t// otherwise surface as a successful \"no issues\" rather than a warning + fetchFailed. The first\n\t\t\t// reason is re-thrown as-is (not wrapped in an AggregateError) so the facade can still classify it\n\t\t\t// by type (auth/rate-limit) — wrapping would collapse every failure to a generic 'other'. The\n\t\t\t// remaining reasons would otherwise be discarded, so log them here to keep them diagnosable.\n\t\t\tif (settled.every(r => r.status === 'rejected')) {\n\t\t\t\tfor (let i = 1; i < settled.length; i++) {\n\t\t\t\t\tconst outcome = settled[i];\n\t\t\t\t\tif (outcome.status === 'rejected') {\n\t\t\t\t\t\tLogger.error(\n\t\t\t\t\t\t\toutcome.reason,\n\t\t\t\t\t\t\t`getProviderIssuesForProjectWithTruncation: filter '${filters[i]}' failed`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow settled[0].status === 'rejected' ? settled[0].reason : new Error('Jira issue read failed');\n\t\t\t}\n\n\t\t\tlet truncated = false;\n\t\t\tlet metadata: CollectionMetadata | undefined;\n\t\t\tconst resultsById = new Map<string, IssueShape>();\n\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\tconst outcome = settled[i];\n\t\t\t\tconst filter = filters[i];\n\t\t\t\t// A rejected filter branch (with at least one sibling succeeding) means this project's issues are\n\t\t\t\t// incomplete: keep the sibling results but record a structured failure so the facade can warn on\n\t\t\t\t// the specific filter (auth/rate-limit) instead of just a generic truncation flag.\n\t\t\t\tif (outcome.status !== 'fulfilled') {\n\t\t\t\t\t// Identical for every filter branch, so degrading it would report one failure per branch for a\n\t\t\t\t\t// single invalid call — see `throwIfCallerContractError`.\n\t\t\t\t\tthrowIfCallerContractError(outcome.reason);\n\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tconst failure = toCollectionScopeFailure(\n\t\t\t\t\t\t{ providerId: this.id, resourceId: project.resourceId, projectId: project.name },\n\t\t\t\t\t\toutcome.reason,\n\t\t\t\t\t);\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t...failure,\n\t\t\t\t\t\t\t\tmessage: `Issue filter '${filter}' could not be read${\n\t\t\t\t\t\t\t\t\tfailure.message != null ? `: ${failure.message}` : ''\n\t\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (outcome.value.status !== 'complete') {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t}\n\t\t\t\tif (outcome.value.metadata != null) {\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, outcome.value.metadata);\n\t\t\t\t}\n\t\t\t\tfor (const resultIssue of outcome.value.issues) {\n\t\t\t\t\tif (!resultsById.has(resultIssue.id)) {\n\t\t\t\t\t\tresultsById.set(resultIssue.id, resultIssue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn truncated\n\t\t\t\t? { values: [...resultsById.values()], truncated: true, recovery: 'none', metadata: metadata }\n\t\t\t\t: { values: [...resultsById.values()], truncated: false, metadata: metadata };\n\t\t}\n\n\t\tconst unscoped = await drainIssues({});\n\t\tconst values = unscoped.issues\n\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t.filter((result): result is IssueShape => result !== undefined);\n\t\treturn unscoped.status !== 'complete'\n\t\t\t? {\n\t\t\t\t\tvalues: values,\n\t\t\t\t\ttruncated: true,\n\t\t\t\t\trecovery: unscoped.status === 'backstop' ? 'narrow-scope' : 'none',\n\t\t\t\t\tmetadata: unscoped.metadata,\n\t\t\t\t}\n\t\t\t: { values: values, truncated: false, metadata: unscoped.metadata };\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: JiraOrganizationDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\tconst myResources = resources ?? (await this.getProviderResourcesForUser(session));\n\t\tif (!myResources) return undefined;\n\n\t\tconst api = await this.getProvidersApi();\n\n\t\tconst results: IssueShape[] = [];\n\t\tfor (const resource of myResources) {\n\t\t\tif (cancellation?.aborted) break;\n\n\t\t\ttry {\n\t\t\t\tlet cursor = undefined;\n\t\t\t\tlet hasMore = false;\n\t\t\t\tlet requestCount = 0;\n\t\t\t\tdo {\n\t\t\t\t\tif (cancellation?.aborted) break;\n\n\t\t\t\t\tconst resourceIssues = await api.getIssuesForResourceForCurrentUser(\n\t\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\t\tresource.id,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\trequestCount += 1;\n\t\t\t\t\thasMore = resourceIssues.paging?.more ?? false;\n\t\t\t\t\tcursor = resourceIssues.paging?.cursor;\n\t\t\t\t\tconst formattedIssues = resourceIssues.values\n\t\t\t\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t\t\t\t.filter((result): result is IssueShape => result != null);\n\t\t\t\t\tif (formattedIssues.length > 0) {\n\t\t\t\t\t\tresults.push(...formattedIssues);\n\t\t\t\t\t}\n\t\t\t\t} while (requestCount < maxPagesPerRequest && hasMore);\n\t\t\t} catch (ex) {\n\t\t\t\t// TODO: We need a better way to message the failure to the user here.\n\t\t\t\t// This is a stopgap to prevent one bag org from throwing and preventing any issues from being returned.\n\t\t\t\tLogger.error(ex, 'searchProviderMyIssues');\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: JiraOrganizationDescriptor,\n\t\t{ key }: { id: string; key: string },\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst issue = await api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resource.id,\n\t\t\tnumber: key,\n\t\t});\n\t\treturn issue != null ? toIssueShape(issue, this) : undefined;\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: JiraOrganizationDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst apiResult = await api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resource.id,\n\t\t\tnumber: id,\n\t\t});\n\t\tconst issue = apiResult != null ? toIssueShape(apiResult, this) : undefined;\n\t\treturn issue != null ? { ...issue, type: 'issue' } : undefined;\n\t}\n\n\tprotected override async getProviderIssueByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresourceId: string,\n\t\tid: string,\n\t\tresourceUrl: string | undefined,\n\t): Promise<Issue | undefined> {\n\t\tif (resourceUrl == null) {\n\t\t\tthrow new Error('Jira direct issue reads require a resource URL');\n\t\t}\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst apiResult = await api.getJiraIssueByKey(toTokenWithInfo(this.id, session), resourceId, resourceUrl, id);\n\t\tconst issue = apiResult != null ? toIssueShape(apiResult, this, { reliableStateCategory: true }) : undefined;\n\t\treturn issue != null ? { ...issue, type: 'issue' } : undefined;\n\t}\n\n\tprotected override async providerOnConnect(): Promise<void> {\n\t\tthis._autolinks = undefined;\n\t\tif (this._session == null) return;\n\n\t\tconst storedOrganizations = this.ctx.storage.get(`jira:${this._session.accessToken}:organizations`);\n\t\tconst storedProjects = this.ctx.storage.get(`jira:${this._session.accessToken}:projects`);\n\n\t\tlet organizations = storedOrganizations?.data?.map((o: JiraOrganizationDescriptor) => ({ ...o }));\n\n\t\tlet projects = storedProjects?.data?.map((p: JiraProjectDescriptor) => ({ ...p }));\n\n\t\tif (storedOrganizations == null) {\n\t\t\torganizations = await this.getProviderResourcesForUser(this._session, true);\n\t\t\t// Clear all other stored organizations and projects when our session changes\n\t\t\tawait this.ctx.storage.deleteWithPrefix('jira');\n\t\t\tawait this.ctx.storage.store(`jira:${this._session.accessToken}:organizations`, {\n\t\t\t\tv: 1,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdata: organizations,\n\t\t\t});\n\t\t}\n\n\t\tthis._organizations ??= new Map<string, JiraOrganizationDescriptor[] | undefined>();\n\t\tthis._organizations.set(this._session.accessToken, organizations);\n\n\t\tif (storedProjects == null && organizations?.length) {\n\t\t\tprojects = await this.getProviderProjectsForResources(this._session, organizations);\n\t\t\tawait this.ctx.storage.store(`jira:${this._session.accessToken}:projects`, {\n\t\t\t\tv: 1,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdata: projects,\n\t\t\t});\n\t\t}\n\n\t\tthis._projects ??= new Map<string, JiraProjectDescriptor[] | undefined>();\n\t\tfor (const project of projects ?? []) {\n\t\t\tconst projectKey = `${this._session.accessToken}:${project.resourceId}`;\n\t\t\tconst projects = this._projects.get(projectKey);\n\t\t\tif (projects == null) {\n\t\t\t\tthis._projects.set(projectKey, [project]);\n\t\t\t} else if (!projects.some(p => p.id === project.id)) {\n\t\t\t\tprojects.push(project);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tthis._organizations = undefined;\n\t\tthis._projects = undefined;\n\t\tthis._autolinks = undefined;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"jira.js","names":[],"sources":["../../../../../plus/integrations/src/providers/jira.ts"],"sourcesContent":["import type { CollectionMetadata } from '@gitkraken/provider-apis';\nimport * as l10n from '@vscode/l10n';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '@gitlens/git/models/autolink.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { IssueResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport type { ProviderRefusal } from '../collectionMetadata.js';\nimport { throwIfCallerContractError, toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { IssuesCloudHostIntegrationId } from '../constants.js';\nimport type { IssuesForProjectOptions, ProjectIssuesDrain } from '../models/issueReads.js';\nimport { IssuesIntegration } from '../models/issuesIntegration.js';\nimport type { ProviderApiCollectionResult, ProviderIssue } from './models.js';\nimport { IssueFilter, providersMetadata, toAccount, toIssueShape } from './models.js';\nimport { collectProviderPagedResult, mergeCollectionMetadata } from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[IssuesCloudHostIntegrationId.Jira];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\nconst maxPagesPerRequest = 10;\n\ntype JiraProjectIssuesDrain<T> = {\n\tissues: T[];\n\tstatus: 'complete' | 'backstop' | 'incomplete';\n\tmetadata?: CollectionMetadata;\n};\n\nexport type JiraBaseDescriptor = IssueResourceDescriptor;\n\nexport interface JiraOrganizationDescriptor extends JiraBaseDescriptor {\n\turl: string;\n\tavatarUrl: string;\n}\n\nexport interface JiraProjectDescriptor extends JiraBaseDescriptor {\n\tresourceId: string;\n}\n\nexport class JiraIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Jira> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\treadonly id = IssuesCloudHostIntegrationId.Jira;\n\tprotected readonly key = this.id;\n\treadonly name: string = 'Jira';\n\n\tget domain(): string {\n\t\treturn metadata.domain;\n\t}\n\n\tprotected get apiBaseUrl(): string {\n\t\treturn 'https://api.atlassian.com';\n\t}\n\n\tprivate _autolinks: Map<string, (AutolinkReference | DynamicAutolinkReference)[]> | undefined;\n\toverride async autolinks(): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected || this._session == null || this._organizations == null || this._projects == null) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst cachedAutolinks = this._autolinks?.get(this._session.accessToken);\n\t\tif (cachedAutolinks != null) return cachedAutolinks;\n\n\t\tconst autolinks: (AutolinkReference | DynamicAutolinkReference)[] = [];\n\t\tconst organizations = this._organizations.get(this._session.accessToken);\n\t\tif (organizations != null) {\n\t\t\tfor (const organization of organizations) {\n\t\t\t\tconst projects = this._projects.get(`${this._session.accessToken}:${organization.id}`);\n\t\t\t\tif (projects != null) {\n\t\t\t\t\tfor (const project of projects) {\n\t\t\t\t\t\tconst dashedPrefix = `${project.key}-`;\n\t\t\t\t\t\tconst underscoredPrefix = `${project.key}_`;\n\t\t\t\t\t\tautolinks.push({\n\t\t\t\t\t\t\tprefix: dashedPrefix,\n\t\t\t\t\t\t\turl: `${organization.url}/browse/${dashedPrefix}<num>`,\n\t\t\t\t\t\t\talphanumeric: false,\n\t\t\t\t\t\t\tignoreCase: false,\n\t\t\t\t\t\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, organization.name),\n\n\t\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\t\tdescription: l10n.t('{0} Issue {1}', organization.name, `${dashedPrefix}<num>`),\n\t\t\t\t\t\t\tdescriptor: { ...organization },\n\t\t\t\t\t\t});\n\t\t\t\t\t\tautolinks.push({\n\t\t\t\t\t\t\tprefix: underscoredPrefix,\n\t\t\t\t\t\t\turl: `${organization.url}/browse/${dashedPrefix}<num>`,\n\t\t\t\t\t\t\talphanumeric: false,\n\t\t\t\t\t\t\tignoreCase: false,\n\t\t\t\t\t\t\treferenceType: 'branch',\n\t\t\t\t\t\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, organization.name),\n\n\t\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\t\tdescription: l10n.t('{0} Issue {1}', organization.name, `${dashedPrefix}<num>`),\n\t\t\t\t\t\t\tdescriptor: { ...organization },\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._autolinks ??= new Map<string, (AutolinkReference | DynamicAutolinkReference)[]>();\n\t\tthis._autolinks.set(this._session.accessToken, autolinks);\n\n\t\treturn autolinks;\n\t}\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: JiraOrganizationDescriptor,\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await api.getCurrentUserForResource(toTokenWithInfo(this.id, session), resource.id);\n\n\t\tif (user == null) return undefined;\n\t\treturn toAccount(user, this);\n\t}\n\n\t/** The site-list request the resource cache would otherwise answer; see `IntegrationBase.validateCredential`. */\n\tprotected override async validateCredential(session: ProviderAuthenticationSession): Promise<void> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif ((await api.getJiraResourcesForCurrentUser(toTokenWithInfo(this.id, session))) == null) {\n\t\t\tthrow new Error('Jira did not confirm the credential');\n\t\t}\n\t}\n\n\t/**\n\t * Atlassian's `401` for a token missing a scope the request needs, which a reconnect fixes by consenting to it\n\t * again; see `IntegrationBase.isCredentialRefusal`. Atlassian gives the same answer to a malformed request URL,\n\t * so a request the SDK built wrong would read as the credential's too.\n\t */\n\tprotected override isCredentialRefusal(refusal: ProviderRefusal): boolean {\n\t\treturn refusal.status === 401 && /scope does not match/i.test(refusal.detail ?? '');\n\t}\n\n\tprivate _organizations: Map<string, JiraOrganizationDescriptor[] | undefined> | undefined;\n\tprotected override async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<JiraOrganizationDescriptor[] | undefined> {\n\t\tconst { accessToken } = session;\n\t\tthis._organizations ??= new Map<string, JiraOrganizationDescriptor[] | undefined>();\n\n\t\tconst cachedResources = this._organizations.get(accessToken);\n\n\t\tif (cachedResources == null || force) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst resources = await api.getJiraResourcesForCurrentUser(toTokenWithInfo(this.id, session));\n\t\t\tthis._organizations.set(\n\t\t\t\taccessToken,\n\t\t\t\tresources != null ? resources.map(r => ({ ...r, key: r.id })) : undefined,\n\t\t\t);\n\t\t}\n\n\t\treturn this._organizations.get(accessToken);\n\t}\n\n\tprivate _projects: Map<string, JiraProjectDescriptor[] | undefined> | undefined;\n\tprotected override async getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: JiraOrganizationDescriptor[],\n\t\tforce: boolean = false,\n\t): Promise<JiraProjectDescriptor[] | undefined> {\n\t\treturn (await this.getProviderProjectsForResourcesWithMetadata(session, resources, force)).values;\n\t}\n\n\tprotected override async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: JiraOrganizationDescriptor[],\n\t\tforce: boolean = false,\n\t): Promise<ProviderApiCollectionResult<JiraProjectDescriptor>> {\n\t\tconst { accessToken } = session;\n\t\tconst projectsCache = (this._projects ??= new Map<string, JiraProjectDescriptor[] | undefined>());\n\n\t\tlet resourcesWithoutProjects = [];\n\t\tif (force) {\n\t\t\tresourcesWithoutProjects = resources;\n\t\t} else {\n\t\t\tfor (const resource of resources) {\n\t\t\t\tconst resourceKey = `${accessToken}:${resource.id}`;\n\t\t\t\tconst cachedProjects = projectsCache.get(resourceKey);\n\t\t\t\tif (cachedProjects == null) {\n\t\t\t\t\tresourcesWithoutProjects.push(resource);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet metadata: CollectionMetadata | undefined;\n\t\tconst partialProjects: JiraProjectDescriptor[] = [];\n\t\tif (resourcesWithoutProjects.length > 0) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t\tconst drains = await Promise.allSettled(\n\t\t\t\tresourcesWithoutProjects.map(async resource => ({\n\t\t\t\t\tresource: resource,\n\t\t\t\t\tresult: await collectProviderPagedResult(\n\t\t\t\t\t\tcursor => api.getJiraProjectsForResource(tokenWithInfo, resource.id, { cursor: cursor }),\n\t\t\t\t\t\tmaxPagesPerRequest,\n\t\t\t\t\t\t{ providerId: this.id, resourceId: resource.id },\n\t\t\t\t\t),\n\t\t\t\t})),\n\t\t\t);\n\n\t\t\tdrains.forEach((drain, index) => {\n\t\t\t\tif (drain.status === 'rejected') {\n\t\t\t\t\tconst resource = resourcesWithoutProjects[index];\n\t\t\t\t\tif (resource == null) return;\n\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure({ providerId: this.id, resourceId: resource.id }, drain.reason),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst { resource, result } = drain.value;\n\t\t\t\tmetadata = mergeCollectionMetadata(metadata, result.metadata);\n\t\t\t\tconst projects = result.values\n\t\t\t\t\t.filter(project => project.resourceId === resource.id)\n\t\t\t\t\t.map(project => ({ ...project }));\n\t\t\t\tconst incomplete =\n\t\t\t\t\tresult.truncated === true ||\n\t\t\t\t\t(result.metadata != null && result.metadata.completeness !== 'complete');\n\t\t\t\tif (incomplete) {\n\t\t\t\t\tpartialProjects.push(...projects);\n\t\t\t\t\tif (\n\t\t\t\t\t\tresult.truncated === true &&\n\t\t\t\t\t\t(result.metadata == null || result.metadata.completeness === 'complete')\n\t\t\t\t\t) {\n\t\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, { completeness: 'partial' });\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tprojectsCache.set(`${accessToken}:${resource.id}`, projects);\n\t\t\t});\n\t\t}\n\n\t\tconst values = resources.reduce<JiraProjectDescriptor[]>((projects, resource) => {\n\t\t\tconst resourceProjects = projectsCache.get(`${accessToken}:${resource.id}`);\n\t\t\tif (resourceProjects != null) {\n\t\t\t\tprojects.push(...resourceProjects);\n\t\t\t}\n\t\t\treturn projects;\n\t\t}, partialProjects);\n\n\t\tconst projectsByIdentity = new Map<string, JiraProjectDescriptor>();\n\t\tfor (const project of values) {\n\t\t\tconst identity = `${project.resourceId}:${project.id}`;\n\t\t\tif (!projectsByIdentity.has(identity)) {\n\t\t\t\tprojectsByIdentity.set(identity, project);\n\t\t\t}\n\t\t}\n\t\treturn { values: [...projectsByIdentity.values()], metadata: metadata };\n\t}\n\n\tprotected override async getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: JiraProjectDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;\n\t}\n\n\tprotected override async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: JiraProjectDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\n\t\tconst api = await this.getProvidersApi();\n\n\t\tconst projectScope = {\n\t\t\tproviderId: this.id,\n\t\t\tresourceId: project.resourceId,\n\t\t\tprojectId: project.name,\n\t\t};\n\t\tconst drainIssues = async (scope: {\n\t\t\tauthorLogin?: string;\n\t\t\tassigneeLogins?: string[];\n\t\t\tmentionLogin?: string;\n\t\t}): Promise<JiraProjectIssuesDrain<ProviderIssue>> => {\n\t\t\tconst collected: ProviderIssue[] = [];\n\t\t\tlet cursor: string | undefined;\n\t\t\tlet status: JiraProjectIssuesDrain<ProviderIssue>['status'] = 'complete';\n\t\t\tlet metadata: CollectionMetadata | undefined;\n\t\t\tfor (let i = 0; i < maxPagesPerRequest; i++) {\n\t\t\t\tlet result: Awaited<ReturnType<typeof api.getIssuesForProjectPaged>> | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tresult = await api.getIssuesForProjectPaged(tokenWithInfo, project.name, project.resourceId, {\n\t\t\t\t\t\t...scope,\n\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\tsort: options?.sort,\n\t\t\t\t\t});\n\t\t\t\t} catch (ex) {\n\t\t\t\t\t// A page failure after the first page leaves the already-drained prefix intact; record the\n\t\t\t\t\t// failure at the project scope instead of re-throwing and discarding the prefix. If nothing was\n\t\t\t\t\t// fetched yet, the original throw behavior is preserved so the caller sees a hard error rather\n\t\t\t\t\t// than an empty partial success.\n\t\t\t\t\tif (collected.length === 0) throw ex;\n\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [toCollectionScopeFailure(projectScope, ex)],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result == null) {\n\t\t\t\t\tif (cursor == null) break;\n\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure(\n\t\t\t\t\t\t\t\tprojectScope,\n\t\t\t\t\t\t\t\tnew Error('Jira returned no page after advertising a continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcollected.push(...result.data);\n\t\t\t\tif (!result.hasMore) break;\n\n\t\t\t\t// The provider claims more pages but gave no advancing cursor: we can't continue, so the drain\n\t\t\t\t// is incomplete — flag it rather than silently stopping (matches drainPullRequests/Repositories).\n\t\t\t\tif (result.nextCursor == null || result.nextCursor === cursor) {\n\t\t\t\t\tstatus = 'incomplete';\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\ttoCollectionScopeFailure(\n\t\t\t\t\t\t\t\tprojectScope,\n\t\t\t\t\t\t\t\tnew Error('Jira returned no advancing issue continuation'),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcursor = result.nextCursor;\n\t\t\t\tif (i === maxPagesPerRequest - 1) {\n\t\t\t\t\tstatus = 'backstop';\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tissues: collected,\n\t\t\t\tstatus: status,\n\t\t\t\tmetadata: metadata,\n\t\t\t};\n\t\t};\n\n\t\tconst getSearchedUserIssuesForFilter = async (\n\t\t\tuser: string,\n\t\t\tfilter: IssueFilter,\n\t\t): Promise<JiraProjectIssuesDrain<IssueShape>> => {\n\t\t\t// `assignee` and `creator` are user FIELDS: Jira resolves an accountId against the directory, which is\n\t\t\t// the identity that keeps matching once a display name cannot be looked up — a deactivated account, or\n\t\t\t// a profile whose visibility hides the name. Measured against a live site: a deactivated assignee\n\t\t\t// returns its issues by accountId and an empty page by display name, and Jira reports that miss as a\n\t\t\t// successful empty search rather than an error, so the list simply appears empty.\n\t\t\t//\n\t\t\t// `mention` is NOT a user field. It is `comment ~ \"...\"`, a free-text search over comment bodies, so an\n\t\t\t// accountId matches nothing there and the display name is the only value that can. Hence the id is\n\t\t\t// applied to the first two and the handle is kept for the third, rather than swapping `user` wholesale.\n\t\t\tconst userField = options?.userId ?? user;\n\t\t\tconst result = await drainIssues({\n\t\t\t\tauthorLogin: filter === IssueFilter.Author ? userField : undefined,\n\t\t\t\tassigneeLogins: filter === IssueFilter.Assignee ? [userField] : undefined,\n\t\t\t\tmentionLogin: filter === IssueFilter.Mention ? user : undefined,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tissues: result.issues\n\t\t\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t\t\t.filter((r): r is IssueShape => r !== undefined),\n\t\t\t\tstatus: result.status,\n\t\t\t\tmetadata: result.metadata,\n\t\t\t};\n\t\t};\n\n\t\tif (options?.user != null) {\n\t\t\tconst user = options.user;\n\t\t\t// A resolved user always scopes the read. Default to the assignee filter (\"my issues\") when no\n\t\t\t// explicit filters are given — otherwise a caller that scopes by user but omits filters would fall\n\t\t\t// through to the unscoped fetch below and get every issue in the project instead of the user's.\n\t\t\tconst filters = options.filters?.length ? options.filters : [IssueFilter.Assignee];\n\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\tfilters.map(filter => getSearchedUserIssuesForFilter(user, filter)),\n\t\t\t);\n\n\t\t\t// If every filter branch rejected, the read failed outright — propagate the first rejection instead\n\t\t\t// of returning an empty list, which the facade (getIssuesForProjectResult → runCaptured) would\n\t\t\t// otherwise surface as a successful \"no issues\" rather than a warning + fetchFailed. The first\n\t\t\t// reason is re-thrown as-is (not wrapped in an AggregateError) so the facade can still classify it\n\t\t\t// by type (auth/rate-limit) — wrapping would collapse every failure to a generic 'other'. The\n\t\t\t// remaining reasons would otherwise be discarded, so log them here to keep them diagnosable.\n\t\t\tif (settled.every(r => r.status === 'rejected')) {\n\t\t\t\tfor (let i = 1; i < settled.length; i++) {\n\t\t\t\t\tconst outcome = settled[i];\n\t\t\t\t\tif (outcome.status === 'rejected') {\n\t\t\t\t\t\tLogger.error(\n\t\t\t\t\t\t\toutcome.reason,\n\t\t\t\t\t\t\t`getProviderIssuesForProjectWithTruncation: filter '${filters[i]}' failed`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow settled[0].status === 'rejected' ? settled[0].reason : new Error('Jira issue read failed');\n\t\t\t}\n\n\t\t\tlet truncated = false;\n\t\t\tlet metadata: CollectionMetadata | undefined;\n\t\t\tconst resultsById = new Map<string, IssueShape>();\n\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\tconst outcome = settled[i];\n\t\t\t\tconst filter = filters[i];\n\t\t\t\t// A rejected filter branch (with at least one sibling succeeding) means this project's issues are\n\t\t\t\t// incomplete: keep the sibling results but record a structured failure so the facade can warn on\n\t\t\t\t// the specific filter (auth/rate-limit) instead of just a generic truncation flag.\n\t\t\t\tif (outcome.status !== 'fulfilled') {\n\t\t\t\t\t// Identical for every filter branch, so degrading it would report one failure per branch for a\n\t\t\t\t\t// single invalid call — see `throwIfCallerContractError`.\n\t\t\t\t\tthrowIfCallerContractError(outcome.reason);\n\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tconst failure = toCollectionScopeFailure(\n\t\t\t\t\t\t{ providerId: this.id, resourceId: project.resourceId, projectId: project.name },\n\t\t\t\t\t\toutcome.reason,\n\t\t\t\t\t);\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, {\n\t\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\t\tfailures: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t...failure,\n\t\t\t\t\t\t\t\tmessage: `Issue filter '${filter}' could not be read${\n\t\t\t\t\t\t\t\t\tfailure.message != null ? `: ${failure.message}` : ''\n\t\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (outcome.value.status !== 'complete') {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t}\n\t\t\t\tif (outcome.value.metadata != null) {\n\t\t\t\t\tmetadata = mergeCollectionMetadata(metadata, outcome.value.metadata);\n\t\t\t\t}\n\t\t\t\tfor (const resultIssue of outcome.value.issues) {\n\t\t\t\t\tif (!resultsById.has(resultIssue.id)) {\n\t\t\t\t\t\tresultsById.set(resultIssue.id, resultIssue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn truncated\n\t\t\t\t? { values: [...resultsById.values()], truncated: true, recovery: 'none', metadata: metadata }\n\t\t\t\t: { values: [...resultsById.values()], truncated: false, metadata: metadata };\n\t\t}\n\n\t\tconst unscoped = await drainIssues({});\n\t\tconst values = unscoped.issues\n\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t.filter((result): result is IssueShape => result !== undefined);\n\t\treturn unscoped.status !== 'complete'\n\t\t\t? {\n\t\t\t\t\tvalues: values,\n\t\t\t\t\ttruncated: true,\n\t\t\t\t\trecovery: unscoped.status === 'backstop' ? 'narrow-scope' : 'none',\n\t\t\t\t\tmetadata: unscoped.metadata,\n\t\t\t\t}\n\t\t\t: { values: values, truncated: false, metadata: unscoped.metadata };\n\t}\n\n\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: JiraOrganizationDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\tconst myResources = resources ?? (await this.getProviderResourcesForUser(session));\n\t\tif (!myResources) return undefined;\n\n\t\tconst api = await this.getProvidersApi();\n\n\t\tconst results: IssueShape[] = [];\n\t\tfor (const resource of myResources) {\n\t\t\tif (cancellation?.aborted) break;\n\n\t\t\ttry {\n\t\t\t\tlet cursor = undefined;\n\t\t\t\tlet hasMore = false;\n\t\t\t\tlet requestCount = 0;\n\t\t\t\tdo {\n\t\t\t\t\tif (cancellation?.aborted) break;\n\n\t\t\t\t\tconst resourceIssues = await api.getIssuesForResourceForCurrentUser(\n\t\t\t\t\t\ttoTokenWithInfo(this.id, session),\n\t\t\t\t\t\tresource.id,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcursor: cursor,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\trequestCount += 1;\n\t\t\t\t\thasMore = resourceIssues.paging?.more ?? false;\n\t\t\t\t\tcursor = resourceIssues.paging?.cursor;\n\t\t\t\t\tconst formattedIssues = resourceIssues.values\n\t\t\t\t\t\t.map(issue => toIssueShape(issue, this))\n\t\t\t\t\t\t.filter((result): result is IssueShape => result != null);\n\t\t\t\t\tif (formattedIssues.length > 0) {\n\t\t\t\t\t\tresults.push(...formattedIssues);\n\t\t\t\t\t}\n\t\t\t\t} while (requestCount < maxPagesPerRequest && hasMore);\n\t\t\t} catch (ex) {\n\t\t\t\t// TODO: We need a better way to message the failure to the user here.\n\t\t\t\t// This is a stopgap to prevent one bag org from throwing and preventing any issues from being returned.\n\t\t\t\tLogger.error(ex, 'searchProviderMyIssues');\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: JiraOrganizationDescriptor,\n\t\t{ key }: { id: string; key: string },\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst issue = await api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resource.id,\n\t\t\tnumber: key,\n\t\t});\n\t\treturn issue != null ? toIssueShape(issue, this) : undefined;\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: JiraOrganizationDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst apiResult = await api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resource.id,\n\t\t\tnumber: id,\n\t\t});\n\t\tconst issue = apiResult != null ? toIssueShape(apiResult, this) : undefined;\n\t\treturn issue != null ? { ...issue, type: 'issue' } : undefined;\n\t}\n\n\tprotected override async getProviderIssueByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresourceId: string,\n\t\tid: string,\n\t\tresourceUrl: string | undefined,\n\t): Promise<Issue | undefined> {\n\t\tif (resourceUrl == null) {\n\t\t\tthrow new Error('Jira direct issue reads require a resource URL');\n\t\t}\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst apiResult = await api.getJiraIssueByKey(toTokenWithInfo(this.id, session), resourceId, resourceUrl, id);\n\t\tconst issue = apiResult != null ? toIssueShape(apiResult, this, { reliableStateCategory: true }) : undefined;\n\t\treturn issue != null ? { ...issue, type: 'issue' } : undefined;\n\t}\n\n\tprotected override async providerOnConnect(): Promise<void> {\n\t\tthis._autolinks = undefined;\n\t\tif (this._session == null) return;\n\n\t\tconst storedOrganizations = this.ctx.storage.get(`jira:${this._session.accessToken}:organizations`);\n\t\tconst storedProjects = this.ctx.storage.get(`jira:${this._session.accessToken}:projects`);\n\n\t\tlet organizations = storedOrganizations?.data?.map((o: JiraOrganizationDescriptor) => ({ ...o }));\n\n\t\tlet projects = storedProjects?.data?.map((p: JiraProjectDescriptor) => ({ ...p }));\n\n\t\tif (storedOrganizations == null) {\n\t\t\torganizations = await this.getProviderResourcesForUser(this._session, true);\n\t\t\t// Clear all other stored organizations and projects when our session changes\n\t\t\tawait this.ctx.storage.deleteWithPrefix('jira');\n\t\t\tawait this.ctx.storage.store(`jira:${this._session.accessToken}:organizations`, {\n\t\t\t\tv: 1,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdata: organizations,\n\t\t\t});\n\t\t}\n\n\t\tthis._organizations ??= new Map<string, JiraOrganizationDescriptor[] | undefined>();\n\t\tthis._organizations.set(this._session.accessToken, organizations);\n\n\t\tif (storedProjects == null && organizations?.length) {\n\t\t\tprojects = await this.getProviderProjectsForResources(this._session, organizations);\n\t\t\tawait this.ctx.storage.store(`jira:${this._session.accessToken}:projects`, {\n\t\t\t\tv: 1,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tdata: projects,\n\t\t\t});\n\t\t}\n\n\t\tthis._projects ??= new Map<string, JiraProjectDescriptor[] | undefined>();\n\t\tfor (const project of projects ?? []) {\n\t\t\tconst projectKey = `${this._session.accessToken}:${project.resourceId}`;\n\t\t\tconst projects = this._projects.get(projectKey);\n\t\t\tif (projects == null) {\n\t\t\t\tthis._projects.set(projectKey, [project]);\n\t\t\t} else if (!projects.some(p => p.id === project.id)) {\n\t\t\t\tprojects.push(project);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected override providerOnDisconnect(): void {\n\t\tthis._organizations = undefined;\n\t\tthis._projects = undefined;\n\t\tthis._autolinks = undefined;\n\t}\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { Logger as _logger } from "../../../utils/logger.js";
2
2
  import { isIssueResourceDescriptor } from "../../../git/utils/resourceDescriptor.utils.js";
3
3
  import { IntegrationReadUnavailableError } from "../errors.js";
4
- import { toCollectionScopeFailure } from "../collectionMetadata.js";
5
4
  import "../constants.js";
5
+ import { toCollectionScopeFailure } from "../collectionMetadata.js";
6
6
  import { toTokenWithInfo } from "../authentication/models.js";
7
- import { fromProviderIssue, providersMetadata, toIssueShape } from "./models.js";
8
7
  import { mergeCollectionMetadata } from "./utils/providerPaging.js";
8
+ import { fromProviderIssue, providersMetadata, toIssueShape } from "./models.js";
9
9
  import { IssuesIntegration } from "../models/issuesIntegration.js";
10
10
  import * as l10n from "@vscode/l10n";
11
11
  //#region ../plus/integrations/src/providers/linear.ts
@@ -7,8 +7,8 @@ import { PullRequest, PullRequestFilter, PullRequestMergeableState, PullRequestR
7
7
  import { Account as Account$1 } from "../../../git/models/author.js";
8
8
  import { IntegrationIds } from "../constants.js";
9
9
  import { IssueFilter } from "../providerFilters.js";
10
- import { Integration, IntegrationType } from "../models/integration.js";
11
10
  import { ProviderOrganization, ProviderRepositoryShape } from "../results.js";
11
+ import { Integration, IntegrationType } from "../models/integration.js";
12
12
  import { ProviderPullRequestReviews } from "./pullRequestReviews.js";
13
13
  import { Account, ActionablePullRequest, AnyEntityIdentifierInput, AzureDevOps, AzureOrganization, AzureProject, AzureSetPullRequestInput, Bitbucket, BitbucketServer, BitbucketWorkspaceStub, CollectionMetadata, CursorPageInput, EnterpriseOptions, GetRepoInput, GitBuildStatusState as GitBuildStatusState$1, GitHub, GitHubPullRequestFieldMap, GitIssueState as GitIssueState$1, GitLab, GitLabGroup, GitMergeStrategy, GitPullRequest, GitPullRequestMergeableState as GitPullRequestMergeableState$1, GitPullRequestReviewState as GitPullRequestReviewState$1, GitPullRequestState as GitPullRequestState$1, GitRepository, Issue, Jira, JiraProject, JiraResource, JiraServer, JiraServerProject, Linear, LinearOrganization, LinearTeam, NumberedPageInput, Organization, PullRequestWithUniqueID, RequestFunction, RequestOptions, Response, SetPullRequestInput, Trello } from "@gitkraken/provider-apis";
14
14
  //#region ../plus/integrations/src/providers/models.d.ts
@@ -673,6 +673,15 @@ export interface ProviderMetadata {
673
673
  * instead of quoting a limit that was never published.
674
674
  */
675
675
  issueSearchResultLimit?: number;
676
+ /**
677
+ * Whether the filtered searches reach the provider with scope names verbatim — as encoded URL segments, or
678
+ * matched against the names discovery reported — rather than inside a query string the provider sanitizes.
679
+ *
680
+ * Relaxes the scope-name rule (`isUsableSearchScopeName`) for that provider to refuse only blank names and
681
+ * control characters: a space or a quote can't split or alter a scope that is never parsed as a query, and
682
+ * refusing them would refuse real names. Absent for a query-string search (GitHub's), where both do.
683
+ */
684
+ exactSearchScopeNames?: boolean;
676
685
  }
677
686
  export type Providers = Record<IntegrationIds, ProviderInfo>;
678
687
  export type ProvidersMetadata = Record<IntegrationIds, ProviderMetadata>;
@@ -6,6 +6,7 @@ import "../constants.js";
6
6
  import { githubSearchResultLimit } from "../../git-github/api/config.js";
7
7
  import { IssueFilter } from "../providerFilters.js";
8
8
  import { fromProviderAccount, toProviderAccount } from "./accounts.js";
9
+ import { azurePullRequestSearchSorts, azureWorkItemSearchRelationships, azureWorkItemSearchResultLimit, azureWorkItemSearchSorts } from "./azure/search.js";
9
10
  import { azureAccountWideIssueSorts, azureIssueSorts, githubAccountWideIssueSorts, githubIssueSorts, gitlabAccountWideIssueSorts, gitlabIssueSorts, jiraIssueSorts, linearIssueSorts, trelloIssueSorts } from "./issueSorts.js";
10
11
  import { fromPullRequestReviewDecision, toCompletedReviews, toProviderReviewDecision, toProviderReviews, toReviewRequests } from "./pullRequestReviews.js";
11
12
  import { getEntityIdentifierInput } from "./utils.js";
@@ -145,6 +146,86 @@ const githubPullRequestSearchCapabilities = {
145
146
  "created:asc"
146
147
  ]
147
148
  };
149
+ /**
150
+ * Bitbucket Data Center's filtered pull-request search (`bitbucket-server/pullRequestSearch.ts`), declared from what
151
+ * its REST API can express rather than from what a PR shape carries:
152
+ * - relationships: `role=AUTHOR`, and a reviewer's `UNAPPROVED` status for a pending review versus `APPROVED` or
153
+ * `NEEDS_WORK` for one already given. A Bitbucket pull request has no assignee, and nothing filters by mention.
154
+ * - text is `filterText` (title or description), draft is `draft`, and archived repositories are excluded unless
155
+ * asked for — all re-checked on each row, since the dashboard read has neither filter.
156
+ * - no date filters and no `created` ordering: `order` is only `NEWEST`/`OLDEST` by last update, and filtering a
157
+ * date on the rows read would page through everything before it.
158
+ * - no organization scope: there is no project-wide pull-request list, only per repository and the dashboard.
159
+ */
160
+ const bitbucketServerPullRequestSearchCapabilities = {
161
+ relationships: [
162
+ "author",
163
+ "review-requested",
164
+ "reviewed-by"
165
+ ],
166
+ states: [
167
+ "open",
168
+ "closed",
169
+ "merged",
170
+ "all"
171
+ ],
172
+ text: true,
173
+ updatedAfter: false,
174
+ createdAfter: false,
175
+ includeArchived: true,
176
+ draft: true,
177
+ repositoryScope: true,
178
+ organizationScope: false,
179
+ sorts: ["updated:desc", "updated:asc"]
180
+ };
181
+ /**
182
+ * Azure DevOps Server's filtered pull-request search. Azure's pull request query narrows only by creator, reviewer,
183
+ * status and repository, so the provider drains each facet and applies text, draft and dates to the complete facet
184
+ * before merging — so a criterion never narrows a page after its count, and the only sort keys are the two a merged
185
+ * page can be re-ordered by. What text and `updatedAfter` match is bounded by the list endpoint: see
186
+ * `toAzurePullRequestSearchFilter`.
187
+ *
188
+ * `Assignee` and `ReviewRequested` both read by reviewer: Azure has no assignee distinct from its reviewers.
189
+ * `Reviewed` and `Mention` are absent because the pull request query has no axis for either, and `includeArchived`
190
+ * because Azure has no archived repositories to exclude.
191
+ */
192
+ const azureServerPullRequestSearchCapabilities = {
193
+ relationships: [
194
+ "author",
195
+ "assignee",
196
+ "review-requested"
197
+ ],
198
+ states: [
199
+ "open",
200
+ "closed",
201
+ "merged",
202
+ "all"
203
+ ],
204
+ text: true,
205
+ updatedAfter: true,
206
+ createdAfter: true,
207
+ includeArchived: false,
208
+ draft: true,
209
+ repositoryScope: true,
210
+ organizationScope: true,
211
+ sorts: azurePullRequestSearchSorts
212
+ };
213
+ /**
214
+ * Azure DevOps Server's filtered work-item search: one WIQL query per collection, every criterion a clause of it.
215
+ * `mentioned` is absent because `@RecentMentions` only reaches back 30 days; `milestone` and
216
+ * `withoutLinkedPullRequest` because WIQL has no flat-query clause for either. See `azure/search.ts`.
217
+ */
218
+ const azureServerIssueSearchCapabilities = {
219
+ relationships: azureWorkItemSearchRelationships,
220
+ text: true,
221
+ labels: true,
222
+ milestone: false,
223
+ updatedAfter: true,
224
+ createdAfter: true,
225
+ withoutLinkedPullRequest: false,
226
+ states: true,
227
+ sorts: azureWorkItemSearchSorts
228
+ };
148
229
  const providersMetadata = {
149
230
  ["github"]: {
150
231
  domain: "github.com",
@@ -312,6 +393,7 @@ const providersMetadata = {
312
393
  iconKey: "bitbucket-server",
313
394
  supportedPullRequestFilters: ["author", "review-requested"],
314
395
  supportedAccountWidePullRequestFilters: ["author", "review-requested"],
396
+ supportedPullRequestSearch: bitbucketServerPullRequestSearchCapabilities,
315
397
  scopes: ["Project (Read)", "Repository (Write)"]
316
398
  },
317
399
  ["azureDevOps"]: {
@@ -374,6 +456,10 @@ const providersMetadata = {
374
456
  supportedAccountWideIssueFilters: ["author", "assignee"],
375
457
  supportedIssueSorts: azureIssueSorts,
376
458
  supportedAccountWideIssueSorts: azureAccountWideIssueSorts,
459
+ supportedPullRequestSearch: azureServerPullRequestSearchCapabilities,
460
+ supportedIssueSearch: azureServerIssueSearchCapabilities,
461
+ issueSearchResultLimit: azureWorkItemSearchResultLimit,
462
+ exactSearchScopeNames: true,
377
463
  scopes: [
378
464
  "vso.code",
379
465
  "vso.identity",