@gitkraken/core-gitlens 0.7.0 → 0.7.1

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 (115) hide show
  1. package/CHANGELOG.md +21 -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/git-cli/exec/git.js +1 -1
  5. package/dist/git-cli/providers/config.js +2 -2
  6. package/dist/git-cli/providers/pausedOperations.js +2 -2
  7. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  8. package/dist/git-cli/providers/staging.js +1 -1
  9. package/dist/ipc/discovery.js +1 -1
  10. package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
  11. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
  12. package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -1
  13. package/dist/plus/integrations/authentication/configuredIntegrationService.js +9 -2
  14. package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
  15. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +1 -1
  16. package/dist/plus/integrations/index.d.ts +2 -2
  17. package/dist/plus/integrations/index.js.map +1 -1
  18. package/dist/plus/integrations/integrationService.d.ts +34 -0
  19. package/dist/plus/integrations/integrationService.js +90 -43
  20. package/dist/plus/integrations/integrationService.js.map +1 -1
  21. package/dist/plus/integrations/lite.js +5 -4
  22. package/dist/plus/integrations/lite.js.map +1 -1
  23. package/dist/plus/integrations/manager.d.ts +68 -23
  24. package/dist/plus/integrations/models/gitHostIntegration.d.ts +30 -14
  25. package/dist/plus/integrations/models/gitHostIntegration.js +59 -21
  26. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  27. package/dist/plus/integrations/models/integration.d.ts +3 -3
  28. package/dist/plus/integrations/models/integration.js +1 -1
  29. package/dist/plus/integrations/models/integration.js.map +1 -1
  30. package/dist/plus/integrations/models/issueReads.d.ts +17 -0
  31. package/dist/plus/integrations/models/pullRequestReads.d.ts +26 -0
  32. package/dist/plus/integrations/providers/azure/azure.d.ts +66 -1
  33. package/dist/plus/integrations/providers/azure/azure.js +192 -12
  34. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  35. package/dist/plus/integrations/providers/azure/search.d.ts +144 -0
  36. package/dist/plus/integrations/providers/azure/search.js +250 -0
  37. package/dist/plus/integrations/providers/azure/search.js.map +1 -0
  38. package/dist/plus/integrations/providers/azureDevOps.d.ts +54 -35
  39. package/dist/plus/integrations/providers/azureDevOps.js +154 -121
  40. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  41. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +176 -0
  42. package/dist/plus/integrations/providers/azureDevOpsServer.js +560 -0
  43. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -0
  44. package/dist/plus/integrations/providers/bitbucket-server/discovery.d.ts +12 -0
  45. package/dist/plus/integrations/providers/bitbucket-server/discovery.js +128 -0
  46. package/dist/plus/integrations/providers/bitbucket-server/discovery.js.map +1 -0
  47. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +10 -1
  48. package/dist/plus/integrations/providers/bitbucket-server/models.js +3 -2
  49. package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
  50. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +58 -0
  51. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +485 -0
  52. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -0
  53. package/dist/plus/integrations/providers/bitbucket-server.d.ts +52 -3
  54. package/dist/plus/integrations/providers/bitbucket-server.js +96 -20
  55. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  56. package/dist/plus/integrations/providers/bitbucket.js +1 -1
  57. package/dist/plus/integrations/providers/github.d.ts +5 -5
  58. package/dist/plus/integrations/providers/github.js +34 -33
  59. package/dist/plus/integrations/providers/github.js.map +1 -1
  60. package/dist/plus/integrations/providers/gitlab.d.ts +5 -3
  61. package/dist/plus/integrations/providers/gitlab.js +31 -27
  62. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  63. package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
  64. package/dist/plus/integrations/providers/jira-server.d.ts +9 -0
  65. package/dist/plus/integrations/providers/jira-server.js +13 -2
  66. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  67. package/dist/plus/integrations/providers/jira.js +1 -1
  68. package/dist/plus/integrations/providers/linear.js +1 -1
  69. package/dist/plus/integrations/providers/models.d.ts +9 -0
  70. package/dist/plus/integrations/providers/models.js +86 -0
  71. package/dist/plus/integrations/providers/models.js.map +1 -1
  72. package/dist/plus/integrations/providers/providersApi.d.ts +10 -0
  73. package/dist/plus/integrations/providers/providersApi.js +16 -1
  74. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  75. package/dist/plus/integrations/providers/utils.d.ts +12 -1
  76. package/dist/plus/integrations/providers/utils.js +36 -6
  77. package/dist/plus/integrations/providers/utils.js.map +1 -1
  78. package/dist/plus/integrations/reads/broaden.js +5 -5
  79. package/dist/plus/integrations/reads/broaden.js.map +1 -1
  80. package/dist/plus/integrations/reads/counts.d.ts +18 -4
  81. package/dist/plus/integrations/reads/counts.js +66 -15
  82. package/dist/plus/integrations/reads/counts.js.map +1 -1
  83. package/dist/plus/integrations/reads/cursors.js +1 -1
  84. package/dist/plus/integrations/reads/drains.d.ts +1 -0
  85. package/dist/plus/integrations/reads/filters.d.ts +1 -1
  86. package/dist/plus/integrations/reads/filters.js +17 -12
  87. package/dist/plus/integrations/reads/filters.js.map +1 -1
  88. package/dist/plus/integrations/reads/hierarchy.js +1 -1
  89. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  90. package/dist/plus/integrations/reads/issueTracker.js +1 -1
  91. package/dist/plus/integrations/reads/issues.js +1 -1
  92. package/dist/plus/integrations/reads/paging.js +1 -1
  93. package/dist/plus/integrations/reads/resolveRepository.d.ts +12 -11
  94. package/dist/plus/integrations/reads/resolveRepository.js +109 -18
  95. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
  96. package/dist/plus/integrations/reads/searchIssues.js +4 -2
  97. package/dist/plus/integrations/reads/searchIssues.js.map +1 -1
  98. package/dist/plus/integrations/reads/searchPullRequests.js +9 -3
  99. package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
  100. package/dist/plus/integrations/reads/trackerIssue.d.ts +9 -5
  101. package/dist/plus/integrations/reads/trackerIssue.js +15 -9
  102. package/dist/plus/integrations/reads/trackerIssue.js.map +1 -1
  103. package/dist/plus/integrations/reads/warnings.d.ts +6 -2
  104. package/dist/plus/integrations/reads/warnings.js +15 -19
  105. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  106. package/dist/plus/integrations/utils/domain.utils.d.ts +19 -0
  107. package/dist/plus/integrations/utils/domain.utils.js +36 -1
  108. package/dist/plus/integrations/utils/domain.utils.js.map +1 -1
  109. package/dist/plus/integrations/utils/integration.utils.d.ts +1 -0
  110. package/dist/plus/integrations/utils/integration.utils.js +12 -1
  111. package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
  112. package/dist/utils/env/node/exec.js +2 -2
  113. package/docs/integrations.md +176 -73
  114. package/docs/kepler-read-api-parity.md +19 -6
  115. package/package.json +1 -1
@@ -78,15 +78,16 @@ function stripScheme(domain) {
78
78
  * GitHub's base URL, which `@gitkraken/provider-apis` derives BOTH the REST and the GraphQL endpoint from by
79
79
  * appending GitHub Enterprise's paths (`/api/v3`, `/api/graphql`). Cloud must therefore pass nothing rather than
80
80
  * `https://api.github.com`, which would build `https://api.github.com/api/graphql` and get a 404; omitting it is
81
- * what selects the cloud endpoints. Mirrors the cloud `GitHubIntegration.apiBaseUrl` getter.
81
+ * what selects the cloud endpoints. Mirrors the cloud `GitHubIntegration.apiBaseUrlFor`.
82
82
  */
83
83
  function resolveGitHubApiBaseUrl(id, host) {
84
84
  return id === "github" ? void 0 : `https://${host}/api/v3`;
85
85
  }
86
86
  /**
87
- * Mirrors the per-provider `apiBaseUrl` getters on the `GitHostIntegration` subclasses, for every family whose
88
- * base URL is always concrete (GitHub's is not — see {@link resolveGitHubApiBaseUrl}). `rawDomain` may carry a
89
- * scheme (only Azure DevOps Server honors it); `host` is the bare host used everywhere else.
87
+ * Mirrors the per-provider `apiBaseUrlFor` methods on the `GitHostIntegration` subclasses for an installation at
88
+ * the host root. Covers every family whose base URL is always concrete (GitHub's is not — see
89
+ * {@link resolveGitHubApiBaseUrl}). `rawDomain` may carry a scheme (only Azure DevOps Server honors it); `host` is
90
+ * the bare host used everywhere else.
90
91
  */
91
92
  function resolveApiBaseUrl(id, rawDomain, host) {
92
93
  switch (id) {
@@ -1 +1 @@
1
- {"version":3,"file":"lite.js","names":[],"sources":["../../../../plus/integrations/src/lite.ts"],"sourcesContent":["// A lightweight, token-scoped entry point for stateless single-shot provider reads.\n//\n// Unlike `createIntegrationManager`, this does NOT build an `IntegrationServiceContext`\n// (no storage/account/config/cache/repositories/hooks) and runs no session/OAuth lifecycle.\n// A consumer that already holds a provider access token (obtained by its own means, e.g.\n// the GitKraken account backend's `v1/provider-tokens/{provider}` endpoint) can call the\n// per-provider API client directly and get back the same DTOs the session-managed\n// `GitHostIntegration` returns.\n//\n// Provider notes:\n// - Azure DevOps encodes the repository as the composite string `\"{project}/_git/{repoName}\"`\n// in the `repo` argument (matching the descriptor `GitHostIntegration` uses); `owner` is the\n// organization. Pass `repo` in that shape for Azure.\n// - Bitbucket Cloud `getDefaultBranch`/`getRepositoryMetadata` depend on `mainbranch`/`parent`\n// fields the API may omit for some repos; they resolve to `undefined` when absent.\n// - Bitbucket Server (`bitbucket-server`) is not supported here — its reads use a different\n// REST surface that this token-scoped path doesn't wire up.\n\nimport type { GitHubApiConfig } from '@gitlens/git-github/api/config.js';\nimport { GitHubApi } from '@gitlens/git-github/api/github.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { TokenWithInfo } from './authentication/models.js';\nimport { toTokenInfo } from './authentication/models.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from './constants.js';\nimport type { ProviderApiConfig } from './providers/apiConfig.js';\nimport { AzureDevOpsApi } from './providers/azure/azure.js';\nimport { BitbucketApi } from './providers/bitbucket/bitbucket.js';\nimport { GitLabApi } from './providers/gitlab/gitlab.js';\nimport { providersMetadata } from './providers/models.js';\n\n/** The git-hosting integrations that expose a token-scoped read path here. */\nexport type TokenScopedGitHostId =\n\t| GitCloudHostIntegrationId.GitHub\n\t| GitCloudHostIntegrationId.GitLab\n\t| GitCloudHostIntegrationId.Bitbucket\n\t| GitCloudHostIntegrationId.AzureDevOps\n\t| GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t| GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted\n\t| GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\nexport interface TokenScopedGitHostToken {\n\t/** The provider access token the consumer already holds. */\n\treadonly accessToken: string;\n\t/**\n\t * Host domain for self-managed instances (e.g. `git.example.com`). Ignored for cloud ids. For an Azure\n\t * DevOps Server reachable over plain HTTP, include the scheme (e.g. `http://tfs.example.com`); otherwise\n\t * `https` is assumed.\n\t */\n\treadonly domain?: string;\n\t/** Optional scopes carried by the token, surfaced only in logging metadata. */\n\treadonly scopes?: readonly string[];\n\t/** Whether the token came from a GK-cloud connection (vs a raw PAT). Defaults to `true`. */\n\treadonly cloud?: boolean;\n}\n\nexport interface TokenScopedHttpConfig {\n\t/** The `fetch` implementation to use for HTTP requests. */\n\tfetch(input: string | URL, init?: RequestInit): Promise<Response>;\n\t/**\n\t * Wrap an async op so TLS validation is disabled when `ignoreSSLErrors` is `'force'`. Optional —\n\t * defaults to a pass-through, which is correct for the browser and for any consumer not toggling SSL.\n\t */\n\twrapForForcedInsecureSSL?<T>(ignoreSSLErrors: boolean | 'force', fn: () => Promise<T> | PromiseLike<T>): Promise<T>;\n\t/** Whether to ignore SSL certificate errors on requests. Defaults to `false`. */\n\tignoreSSLErrors?: boolean | 'force';\n\t/**\n\t * Whether the consumer runs in a web worker / browser (vs Node.js). GitHub uses this to drop the\n\t * `user-agent` header, which browsers reject and warn about. Defaults to `false`.\n\t */\n\tisWeb?: boolean;\n}\n\n/** The stateless reads a token-scoped integration exposes. */\nexport interface TokenScopedGitHostIntegration {\n\tgetRepositoryMetadata(\n\t\towner: string,\n\t\trepo: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined>;\n\tgetDefaultBranch(owner: string, repo: string, cancellation?: AbortSignal): Promise<DefaultBranch | undefined>;\n}\n\n/**\n * Build a token-scoped git-host integration for a single provider, backed only by a `fetch` + a token —\n * no `IntegrationServiceContext`, no session/OAuth lifecycle. The returned DTOs match those from the\n * session-managed {@link GitHostIntegration}.\n */\nexport function createTokenScopedGitHostIntegration(\n\tid: TokenScopedGitHostId,\n\ttoken: TokenScopedGitHostToken,\n\thttp: TokenScopedHttpConfig,\n): TokenScopedGitHostIntegration {\n\t// `token.domain` only applies to self-managed ids; cloud ids always use their canonical domain.\n\tconst selfManaged =\n\t\tid === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise ||\n\t\tid === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted ||\n\t\tid === GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\t// Self-managed ids carry no canonical domain (`providersMetadata[id].domain` is empty), so a missing\n\t// `token.domain` would silently build a malformed base URL (e.g. `https:///api/v3`). Fail fast instead.\n\tif (selfManaged && !token.domain) {\n\t\tthrow new Error(`A token-scoped integration for the self-managed host '${id}' requires 'token.domain'.`);\n\t}\n\n\tconst rawDomain = (selfManaged && token.domain) || providersMetadata[id].domain;\n\t// The provider/DTO domain drops the scheme (a subpath/collection is preserved for Azure Server and subpath\n\t// installs); only Azure DevOps Server's base URL honors an explicit scheme (per its doc). Stripping keeps a\n\t// scheme out of the DTO and prevents a malformed base URL (e.g. `https://http://host/api/v3`) if one is\n\t// accidentally passed for GHE/GitLab.\n\tconst host = stripScheme(rawDomain);\n\tconst provider = createProviderStub(id, host, http.ignoreSSLErrors ?? false);\n\tconst tokenInfo = buildTokenWithInfo(id, token);\n\n\tconst wrapForForcedInsecureSSL: NonNullable<TokenScopedHttpConfig['wrapForForcedInsecureSSL']> =\n\t\t// `Promise.resolve().then(fn)` (not `Promise.resolve(fn())`) so a synchronous throw in `fn` surfaces as\n\t\t// a rejected promise, honoring the `Promise<T>` contract.\n\t\thttp.wrapForForcedInsecureSSL ?? ((_ignore, fn) => Promise.resolve().then(fn));\n\n\t// GitHub has its own VS Code-free config type and a `getDefaultBranch` without a cancellation param.\n\tif (id === GitCloudHostIntegrationId.GitHub || id === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise) {\n\t\tconst config: GitHubApiConfig = {\n\t\t\tisWeb: http.isWeb ?? false,\n\t\t\tfetch: (url, init) => http.fetch(url, init),\n\t\t\twrapForForcedInsecureSSL: wrapForForcedInsecureSSL,\n\t\t};\n\t\tconst api = new GitHubApi(config);\n\t\t// GitHub is the one family whose base URL can legitimately be absent — see `resolveGitHubApiBaseUrl`.\n\t\tconst baseUrl = resolveGitHubApiBaseUrl(id, host);\n\t\treturn {\n\t\t\tgetRepositoryMetadata: (owner, repo, cancellation) =>\n\t\t\t\tapi.getRepositoryMetadata(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t\t\t// `GitHubApi.getDefaultBranch` has no cancellation param; accept and intentionally ignore it here so\n\t\t\t// the signature matches the other providers and the drop is explicit at the call site.\n\t\t\tgetDefaultBranch: (owner, repo, _cancellation) =>\n\t\t\t\tapi.getDefaultBranch(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }),\n\t\t};\n\t}\n\n\t// GitLab, Bitbucket, and Azure DevOps share the `ProviderApiConfig` client shape and identical read signatures.\n\tconst baseUrl = resolveApiBaseUrl(id, rawDomain, host);\n\tconst config = toProviderApiConfig(http, wrapForForcedInsecureSSL);\n\tconst api =\n\t\tid === GitCloudHostIntegrationId.GitLab || id === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted\n\t\t\t? new GitLabApi(config)\n\t\t\t: id === GitCloudHostIntegrationId.Bitbucket\n\t\t\t\t? new BitbucketApi(config)\n\t\t\t\t: new AzureDevOpsApi(config);\n\treturn {\n\t\tgetRepositoryMetadata: (owner, repo, cancellation) =>\n\t\t\tapi.getRepositoryMetadata(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t\tgetDefaultBranch: (owner, repo, cancellation) =>\n\t\t\tapi.getDefaultBranch(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t};\n}\n\nfunction toProviderApiConfig(\n\thttp: TokenScopedHttpConfig,\n\twrapForForcedInsecureSSL: NonNullable<TokenScopedHttpConfig['wrapForForcedInsecureSSL']>,\n): ProviderApiConfig {\n\treturn {\n\t\tfetch: (input, init) => http.fetch(input, init),\n\t\twrapForForcedInsecureSSL: wrapForForcedInsecureSSL,\n\t};\n}\n\nfunction buildTokenWithInfo(id: TokenScopedGitHostId, token: TokenScopedGitHostToken): TokenWithInfo {\n\tconst cloud = token.cloud ?? true;\n\treturn {\n\t\t...toTokenInfo(id, token.accessToken, { cloud: cloud, type: undefined, scopes: token.scopes }),\n\t\taccessToken: token.accessToken,\n\t};\n}\n\n/** A minimal {@link Provider} for the API clients: identity + no-op reauth/telemetry, SSL from config. */\nfunction createProviderStub(id: TokenScopedGitHostId, domain: string, ignoreSSLErrors: boolean | 'force'): Provider {\n\tconst metadata = providersMetadata[id];\n\treturn {\n\t\tid: id,\n\t\tname: metadata.name,\n\t\tdomain: domain,\n\t\ticon: metadata.iconKey,\n\t\tgetIgnoreSSLErrors: () => ignoreSSLErrors,\n\t\treauthenticate: () => Promise.resolve(),\n\t\ttrackRequestException: () => {},\n\t};\n}\n\n/** Strips a leading `scheme://` and any trailing slashes; a subpath/collection (e.g. `host/tfs/Col`) is preserved. */\nfunction stripScheme(domain: string): string {\n\treturn domain.replace(/^[a-z][\\w+.-]*:\\/\\//i, '').replace(/\\/+$/, '');\n}\n\n/**\n * GitHub's base URL, which `@gitkraken/provider-apis` derives BOTH the REST and the GraphQL endpoint from by\n * appending GitHub Enterprise's paths (`/api/v3`, `/api/graphql`). Cloud must therefore pass nothing rather than\n * `https://api.github.com`, which would build `https://api.github.com/api/graphql` and get a 404; omitting it is\n * what selects the cloud endpoints. Mirrors the cloud `GitHubIntegration.apiBaseUrl` getter.\n */\nfunction resolveGitHubApiBaseUrl(\n\tid: GitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise,\n\thost: string,\n): string | undefined {\n\treturn id === GitCloudHostIntegrationId.GitHub ? undefined : `https://${host}/api/v3`;\n}\n\n/**\n * Mirrors the per-provider `apiBaseUrl` getters on the `GitHostIntegration` subclasses, for every family whose\n * base URL is always concrete (GitHub's is not — see {@link resolveGitHubApiBaseUrl}). `rawDomain` may carry a\n * scheme (only Azure DevOps Server honors it); `host` is the bare host used everywhere else.\n */\nfunction resolveApiBaseUrl(\n\tid: Exclude<\n\t\tTokenScopedGitHostId,\n\t\tGitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t>,\n\trawDomain: string,\n\thost: string,\n): string {\n\tswitch (id) {\n\t\tcase GitCloudHostIntegrationId.GitLab:\n\t\t\treturn 'https://gitlab.com/api';\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\t\treturn `https://${host}/api`;\n\t\tcase GitCloudHostIntegrationId.Bitbucket:\n\t\t\treturn 'https://api.bitbucket.org/2.0';\n\t\tcase GitCloudHostIntegrationId.AzureDevOps:\n\t\t\treturn 'https://dev.azure.com';\n\t\tcase GitSelfManagedHostIntegrationId.AzureDevOpsServer:\n\t\t\t// Mirror the session-managed getter, which honors the connection protocol; default to https.\n\t\t\treturn rawDomain.includes('://') ? rawDomain.replace(/\\/+$/, '') : `https://${host}`;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAyFA,SAAgB,oCACf,IACA,OACA,MACgC;CAEhC,MAAM,cACL,OAAA,6BACA,OAAA,8BACA,OAAA;CAGD,IAAI,eAAe,CAAC,MAAM,QACzB,MAAM,IAAI,MAAM,yDAAyD,GAAG,2BAA2B;CAGxG,MAAM,YAAa,eAAe,MAAM,UAAW,kBAAkB,GAAG,CAAC;CAKzE,MAAM,OAAO,YAAY,SAAS;CAClC,MAAM,WAAW,mBAAmB,IAAI,MAAM,KAAK,mBAAmB,KAAK;CAC3E,MAAM,YAAY,mBAAmB,IAAI,KAAK;CAE9C,MAAM,2BAGL,KAAK,8BAA8B,SAAS,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,EAAE;CAG7E,IAAI,OAAA,YAA2C,OAAA,2BAA8D;EAC5G,MAAM,SAA0B;GAC/B,OAAO,KAAK,SAAS;GACrB,QAAQ,KAAK,SAAS,KAAK,MAAM,KAAK,IAAI;GAChB;EAC3B;EACA,MAAM,MAAM,IAAI,UAAU,MAAM;EAEhC,MAAM,UAAU,wBAAwB,IAAI,IAAI;EAChD,OAAO;GACN,wBAAwB,OAAO,MAAM,iBACpC,IAAI,sBAAsB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;GAG/F,mBAAmB,OAAO,MAAM,kBAC/B,IAAI,iBAAiB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,CAAC;EAC7E;CACD;CAGA,MAAM,UAAU,kBAAkB,IAAI,WAAW,IAAI;CACrD,MAAM,SAAS,oBAAoB,MAAM,wBAAwB;CACjE,MAAM,MACL,OAAA,YAA2C,OAAA,6BACxC,IAAI,UAAU,MAAM,IACpB,OAAA,cACC,IAAI,aAAa,MAAM,IACvB,IAAI,eAAe,MAAM;CAC9B,OAAO;EACN,wBAAwB,OAAO,MAAM,iBACpC,IAAI,sBAAsB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;EAC/F,mBAAmB,OAAO,MAAM,iBAC/B,IAAI,iBAAiB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;CAC3F;AACD;AAEA,SAAS,oBACR,MACA,0BACoB;CACpB,OAAO;EACN,QAAQ,OAAO,SAAS,KAAK,MAAM,OAAO,IAAI;EACpB;CAC3B;AACD;AAEA,SAAS,mBAAmB,IAA0B,OAA+C;CACpG,MAAM,QAAQ,MAAM,SAAS;CAC7B,OAAO;EACN,GAAG,YAAY,IAAI,MAAM,aAAa;GAAS;GAAO,MAAM,KAAA;GAAW,QAAQ,MAAM;EAAO,CAAC;EAC7F,aAAa,MAAM;CACpB;AACD;;AAGA,SAAS,mBAAmB,IAA0B,QAAgB,iBAA8C;CACnH,MAAM,WAAW,kBAAkB;CACnC,OAAO;EACF;EACJ,MAAM,SAAS;EACP;EACR,MAAM,SAAS;EACf,0BAA0B;EAC1B,sBAAsB,QAAQ,QAAQ;EACtC,6BAA6B,CAAC;CAC/B;AACD;;AAGA,SAAS,YAAY,QAAwB;CAC5C,OAAO,OAAO,QAAQ,wBAAwB,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;AACrE;;;;;;;AAQA,SAAS,wBACR,IACA,MACqB;CACrB,OAAO,OAAA,WAA0C,KAAA,IAAY,WAAW,KAAK;AAC9E;;;;;;AAOA,SAAS,kBACR,IAIA,WACA,MACS;CACT,QAAQ,IAAR;EACC,KAAA,UACC,OAAO;EACR,KAAA,4BACC,OAAO,WAAW,KAAK;EACxB,KAAA,aACC,OAAO;EACR,KAAA,eACC,OAAO;EACR,KAAA,uBAEC,OAAO,UAAU,SAAS,KAAK,IAAI,UAAU,QAAQ,QAAQ,EAAE,IAAI,WAAW;CAChF;AACD"}
1
+ {"version":3,"file":"lite.js","names":[],"sources":["../../../../plus/integrations/src/lite.ts"],"sourcesContent":["// A lightweight, token-scoped entry point for stateless single-shot provider reads.\n//\n// Unlike `createIntegrationManager`, this does NOT build an `IntegrationServiceContext`\n// (no storage/account/config/cache/repositories/hooks) and runs no session/OAuth lifecycle.\n// A consumer that already holds a provider access token (obtained by its own means, e.g.\n// the GitKraken account backend's `v1/provider-tokens/{provider}` endpoint) can call the\n// per-provider API client directly and get back the same DTOs the session-managed\n// `GitHostIntegration` returns.\n//\n// Provider notes:\n// - Azure DevOps encodes the repository as the composite string `\"{project}/_git/{repoName}\"`\n// in the `repo` argument (matching the descriptor `GitHostIntegration` uses); `owner` is the\n// organization. Pass `repo` in that shape for Azure.\n// - Bitbucket Cloud `getDefaultBranch`/`getRepositoryMetadata` depend on `mainbranch`/`parent`\n// fields the API may omit for some repos; they resolve to `undefined` when absent.\n// - Bitbucket Server (`bitbucket-server`) is not supported here — its reads use a different\n// REST surface that this token-scoped path doesn't wire up.\n\nimport type { GitHubApiConfig } from '@gitlens/git-github/api/config.js';\nimport { GitHubApi } from '@gitlens/git-github/api/github.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport type { TokenWithInfo } from './authentication/models.js';\nimport { toTokenInfo } from './authentication/models.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from './constants.js';\nimport type { ProviderApiConfig } from './providers/apiConfig.js';\nimport { AzureDevOpsApi } from './providers/azure/azure.js';\nimport { BitbucketApi } from './providers/bitbucket/bitbucket.js';\nimport { GitLabApi } from './providers/gitlab/gitlab.js';\nimport { providersMetadata } from './providers/models.js';\n\n/** The git-hosting integrations that expose a token-scoped read path here. */\nexport type TokenScopedGitHostId =\n\t| GitCloudHostIntegrationId.GitHub\n\t| GitCloudHostIntegrationId.GitLab\n\t| GitCloudHostIntegrationId.Bitbucket\n\t| GitCloudHostIntegrationId.AzureDevOps\n\t| GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t| GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted\n\t| GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\nexport interface TokenScopedGitHostToken {\n\t/** The provider access token the consumer already holds. */\n\treadonly accessToken: string;\n\t/**\n\t * Host domain for self-managed instances (e.g. `git.example.com`). Ignored for cloud ids. For an Azure\n\t * DevOps Server reachable over plain HTTP, include the scheme (e.g. `http://tfs.example.com`); otherwise\n\t * `https` is assumed.\n\t */\n\treadonly domain?: string;\n\t/** Optional scopes carried by the token, surfaced only in logging metadata. */\n\treadonly scopes?: readonly string[];\n\t/** Whether the token came from a GK-cloud connection (vs a raw PAT). Defaults to `true`. */\n\treadonly cloud?: boolean;\n}\n\nexport interface TokenScopedHttpConfig {\n\t/** The `fetch` implementation to use for HTTP requests. */\n\tfetch(input: string | URL, init?: RequestInit): Promise<Response>;\n\t/**\n\t * Wrap an async op so TLS validation is disabled when `ignoreSSLErrors` is `'force'`. Optional —\n\t * defaults to a pass-through, which is correct for the browser and for any consumer not toggling SSL.\n\t */\n\twrapForForcedInsecureSSL?<T>(ignoreSSLErrors: boolean | 'force', fn: () => Promise<T> | PromiseLike<T>): Promise<T>;\n\t/** Whether to ignore SSL certificate errors on requests. Defaults to `false`. */\n\tignoreSSLErrors?: boolean | 'force';\n\t/**\n\t * Whether the consumer runs in a web worker / browser (vs Node.js). GitHub uses this to drop the\n\t * `user-agent` header, which browsers reject and warn about. Defaults to `false`.\n\t */\n\tisWeb?: boolean;\n}\n\n/** The stateless reads a token-scoped integration exposes. */\nexport interface TokenScopedGitHostIntegration {\n\tgetRepositoryMetadata(\n\t\towner: string,\n\t\trepo: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined>;\n\tgetDefaultBranch(owner: string, repo: string, cancellation?: AbortSignal): Promise<DefaultBranch | undefined>;\n}\n\n/**\n * Build a token-scoped git-host integration for a single provider, backed only by a `fetch` + a token —\n * no `IntegrationServiceContext`, no session/OAuth lifecycle. The returned DTOs match those from the\n * session-managed {@link GitHostIntegration}.\n */\nexport function createTokenScopedGitHostIntegration(\n\tid: TokenScopedGitHostId,\n\ttoken: TokenScopedGitHostToken,\n\thttp: TokenScopedHttpConfig,\n): TokenScopedGitHostIntegration {\n\t// `token.domain` only applies to self-managed ids; cloud ids always use their canonical domain.\n\tconst selfManaged =\n\t\tid === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise ||\n\t\tid === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted ||\n\t\tid === GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\t// Self-managed ids carry no canonical domain (`providersMetadata[id].domain` is empty), so a missing\n\t// `token.domain` would silently build a malformed base URL (e.g. `https:///api/v3`). Fail fast instead.\n\tif (selfManaged && !token.domain) {\n\t\tthrow new Error(`A token-scoped integration for the self-managed host '${id}' requires 'token.domain'.`);\n\t}\n\n\tconst rawDomain = (selfManaged && token.domain) || providersMetadata[id].domain;\n\t// The provider/DTO domain drops the scheme (a subpath/collection is preserved for Azure Server and subpath\n\t// installs); only Azure DevOps Server's base URL honors an explicit scheme (per its doc). Stripping keeps a\n\t// scheme out of the DTO and prevents a malformed base URL (e.g. `https://http://host/api/v3`) if one is\n\t// accidentally passed for GHE/GitLab.\n\tconst host = stripScheme(rawDomain);\n\tconst provider = createProviderStub(id, host, http.ignoreSSLErrors ?? false);\n\tconst tokenInfo = buildTokenWithInfo(id, token);\n\n\tconst wrapForForcedInsecureSSL: NonNullable<TokenScopedHttpConfig['wrapForForcedInsecureSSL']> =\n\t\t// `Promise.resolve().then(fn)` (not `Promise.resolve(fn())`) so a synchronous throw in `fn` surfaces as\n\t\t// a rejected promise, honoring the `Promise<T>` contract.\n\t\thttp.wrapForForcedInsecureSSL ?? ((_ignore, fn) => Promise.resolve().then(fn));\n\n\t// GitHub has its own VS Code-free config type and a `getDefaultBranch` without a cancellation param.\n\tif (id === GitCloudHostIntegrationId.GitHub || id === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise) {\n\t\tconst config: GitHubApiConfig = {\n\t\t\tisWeb: http.isWeb ?? false,\n\t\t\tfetch: (url, init) => http.fetch(url, init),\n\t\t\twrapForForcedInsecureSSL: wrapForForcedInsecureSSL,\n\t\t};\n\t\tconst api = new GitHubApi(config);\n\t\t// GitHub is the one family whose base URL can legitimately be absent — see `resolveGitHubApiBaseUrl`.\n\t\tconst baseUrl = resolveGitHubApiBaseUrl(id, host);\n\t\treturn {\n\t\t\tgetRepositoryMetadata: (owner, repo, cancellation) =>\n\t\t\t\tapi.getRepositoryMetadata(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t\t\t// `GitHubApi.getDefaultBranch` has no cancellation param; accept and intentionally ignore it here so\n\t\t\t// the signature matches the other providers and the drop is explicit at the call site.\n\t\t\tgetDefaultBranch: (owner, repo, _cancellation) =>\n\t\t\t\tapi.getDefaultBranch(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }),\n\t\t};\n\t}\n\n\t// GitLab, Bitbucket, and Azure DevOps share the `ProviderApiConfig` client shape and identical read signatures.\n\tconst baseUrl = resolveApiBaseUrl(id, rawDomain, host);\n\tconst config = toProviderApiConfig(http, wrapForForcedInsecureSSL);\n\tconst api =\n\t\tid === GitCloudHostIntegrationId.GitLab || id === GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted\n\t\t\t? new GitLabApi(config)\n\t\t\t: id === GitCloudHostIntegrationId.Bitbucket\n\t\t\t\t? new BitbucketApi(config)\n\t\t\t\t: new AzureDevOpsApi(config);\n\treturn {\n\t\tgetRepositoryMetadata: (owner, repo, cancellation) =>\n\t\t\tapi.getRepositoryMetadata(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t\tgetDefaultBranch: (owner, repo, cancellation) =>\n\t\t\tapi.getDefaultBranch(provider, tokenInfo, owner, repo, { baseUrl: baseUrl }, cancellation),\n\t};\n}\n\nfunction toProviderApiConfig(\n\thttp: TokenScopedHttpConfig,\n\twrapForForcedInsecureSSL: NonNullable<TokenScopedHttpConfig['wrapForForcedInsecureSSL']>,\n): ProviderApiConfig {\n\treturn {\n\t\tfetch: (input, init) => http.fetch(input, init),\n\t\twrapForForcedInsecureSSL: wrapForForcedInsecureSSL,\n\t};\n}\n\nfunction buildTokenWithInfo(id: TokenScopedGitHostId, token: TokenScopedGitHostToken): TokenWithInfo {\n\tconst cloud = token.cloud ?? true;\n\treturn {\n\t\t...toTokenInfo(id, token.accessToken, { cloud: cloud, type: undefined, scopes: token.scopes }),\n\t\taccessToken: token.accessToken,\n\t};\n}\n\n/** A minimal {@link Provider} for the API clients: identity + no-op reauth/telemetry, SSL from config. */\nfunction createProviderStub(id: TokenScopedGitHostId, domain: string, ignoreSSLErrors: boolean | 'force'): Provider {\n\tconst metadata = providersMetadata[id];\n\treturn {\n\t\tid: id,\n\t\tname: metadata.name,\n\t\tdomain: domain,\n\t\ticon: metadata.iconKey,\n\t\tgetIgnoreSSLErrors: () => ignoreSSLErrors,\n\t\treauthenticate: () => Promise.resolve(),\n\t\ttrackRequestException: () => {},\n\t};\n}\n\n/** Strips a leading `scheme://` and any trailing slashes; a subpath/collection (e.g. `host/tfs/Col`) is preserved. */\nfunction stripScheme(domain: string): string {\n\treturn domain.replace(/^[a-z][\\w+.-]*:\\/\\//i, '').replace(/\\/+$/, '');\n}\n\n/**\n * GitHub's base URL, which `@gitkraken/provider-apis` derives BOTH the REST and the GraphQL endpoint from by\n * appending GitHub Enterprise's paths (`/api/v3`, `/api/graphql`). Cloud must therefore pass nothing rather than\n * `https://api.github.com`, which would build `https://api.github.com/api/graphql` and get a 404; omitting it is\n * what selects the cloud endpoints. Mirrors the cloud `GitHubIntegration.apiBaseUrlFor`.\n */\nfunction resolveGitHubApiBaseUrl(\n\tid: GitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise,\n\thost: string,\n): string | undefined {\n\treturn id === GitCloudHostIntegrationId.GitHub ? undefined : `https://${host}/api/v3`;\n}\n\n/**\n * Mirrors the per-provider `apiBaseUrlFor` methods on the `GitHostIntegration` subclasses for an installation at\n * the host root. Covers every family whose base URL is always concrete (GitHub's is not — see\n * {@link resolveGitHubApiBaseUrl}). `rawDomain` may carry a scheme (only Azure DevOps Server honors it); `host` is\n * the bare host used everywhere else.\n */\nfunction resolveApiBaseUrl(\n\tid: Exclude<\n\t\tTokenScopedGitHostId,\n\t\tGitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t>,\n\trawDomain: string,\n\thost: string,\n): string {\n\tswitch (id) {\n\t\tcase GitCloudHostIntegrationId.GitLab:\n\t\t\treturn 'https://gitlab.com/api';\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\t\treturn `https://${host}/api`;\n\t\tcase GitCloudHostIntegrationId.Bitbucket:\n\t\t\treturn 'https://api.bitbucket.org/2.0';\n\t\tcase GitCloudHostIntegrationId.AzureDevOps:\n\t\t\treturn 'https://dev.azure.com';\n\t\tcase GitSelfManagedHostIntegrationId.AzureDevOpsServer:\n\t\t\t// Mirror the session-managed getter, which honors the connection protocol; default to https.\n\t\t\treturn rawDomain.includes('://') ? rawDomain.replace(/\\/+$/, '') : `https://${host}`;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAyFA,SAAgB,oCACf,IACA,OACA,MACgC;CAEhC,MAAM,cACL,OAAA,6BACA,OAAA,8BACA,OAAA;CAGD,IAAI,eAAe,CAAC,MAAM,QACzB,MAAM,IAAI,MAAM,yDAAyD,GAAG,2BAA2B;CAGxG,MAAM,YAAa,eAAe,MAAM,UAAW,kBAAkB,GAAG,CAAC;CAKzE,MAAM,OAAO,YAAY,SAAS;CAClC,MAAM,WAAW,mBAAmB,IAAI,MAAM,KAAK,mBAAmB,KAAK;CAC3E,MAAM,YAAY,mBAAmB,IAAI,KAAK;CAE9C,MAAM,2BAGL,KAAK,8BAA8B,SAAS,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,EAAE;CAG7E,IAAI,OAAA,YAA2C,OAAA,2BAA8D;EAC5G,MAAM,SAA0B;GAC/B,OAAO,KAAK,SAAS;GACrB,QAAQ,KAAK,SAAS,KAAK,MAAM,KAAK,IAAI;GAChB;EAC3B;EACA,MAAM,MAAM,IAAI,UAAU,MAAM;EAEhC,MAAM,UAAU,wBAAwB,IAAI,IAAI;EAChD,OAAO;GACN,wBAAwB,OAAO,MAAM,iBACpC,IAAI,sBAAsB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;GAG/F,mBAAmB,OAAO,MAAM,kBAC/B,IAAI,iBAAiB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,CAAC;EAC7E;CACD;CAGA,MAAM,UAAU,kBAAkB,IAAI,WAAW,IAAI;CACrD,MAAM,SAAS,oBAAoB,MAAM,wBAAwB;CACjE,MAAM,MACL,OAAA,YAA2C,OAAA,6BACxC,IAAI,UAAU,MAAM,IACpB,OAAA,cACC,IAAI,aAAa,MAAM,IACvB,IAAI,eAAe,MAAM;CAC9B,OAAO;EACN,wBAAwB,OAAO,MAAM,iBACpC,IAAI,sBAAsB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;EAC/F,mBAAmB,OAAO,MAAM,iBAC/B,IAAI,iBAAiB,UAAU,WAAW,OAAO,MAAM,EAAW,QAAQ,GAAG,YAAY;CAC3F;AACD;AAEA,SAAS,oBACR,MACA,0BACoB;CACpB,OAAO;EACN,QAAQ,OAAO,SAAS,KAAK,MAAM,OAAO,IAAI;EACpB;CAC3B;AACD;AAEA,SAAS,mBAAmB,IAA0B,OAA+C;CACpG,MAAM,QAAQ,MAAM,SAAS;CAC7B,OAAO;EACN,GAAG,YAAY,IAAI,MAAM,aAAa;GAAS;GAAO,MAAM,KAAA;GAAW,QAAQ,MAAM;EAAO,CAAC;EAC7F,aAAa,MAAM;CACpB;AACD;;AAGA,SAAS,mBAAmB,IAA0B,QAAgB,iBAA8C;CACnH,MAAM,WAAW,kBAAkB;CACnC,OAAO;EACF;EACJ,MAAM,SAAS;EACP;EACR,MAAM,SAAS;EACf,0BAA0B;EAC1B,sBAAsB,QAAQ,QAAQ;EACtC,6BAA6B,CAAC;CAC/B;AACD;;AAGA,SAAS,YAAY,QAAwB;CAC5C,OAAO,OAAO,QAAQ,wBAAwB,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;AACrE;;;;;;;AAQA,SAAS,wBACR,IACA,MACqB;CACrB,OAAO,OAAA,WAA0C,KAAA,IAAY,WAAW,KAAK;AAC9E;;;;;;;AAQA,SAAS,kBACR,IAIA,WACA,MACS;CACT,QAAQ,IAAR;EACC,KAAA,UACC,OAAO;EACR,KAAA,4BACC,OAAO,WAAW,KAAK;EACxB,KAAA,aACC,OAAO;EACR,KAAA,eACC,OAAO;EACR,KAAA,uBAEC,OAAO,UAAU,SAAS,KAAK,IAAI,UAAU,QAAQ,QAAQ,EAAE,IAAI,WAAW;CAChF;AACD"}
@@ -343,7 +343,8 @@ export interface IntegrationManager {
343
343
  * scope.
344
344
  *
345
345
  * This is a separate read from {@link listPullRequestsPage}: free text reaches the provider instead of filtering
346
- * whichever rows happened to be loaded, and every cursor-threaded page costs exactly one upstream request.
346
+ * whichever rows happened to be loaded, and on GitHub/GHE every cursor-threaded page costs exactly one upstream
347
+ * request (Bitbucket Data Center spends one per repository × relationship facet it is still reading).
347
348
  * `criteria.relationships` and `criteria.states` are OR sets, so `[closed, merged]` expresses the complete
348
349
  * terminal set and `[Author, Assignee, ReviewRequested]` matches the visible PR list without falling back to
349
350
  * GitHub's mismatched `involves:@me`. Omit relationships only with a repository/organization scope to search
@@ -353,14 +354,24 @@ export interface IntegrationManager {
353
354
  * criterion — a key not in `getSupportedFilters().pullRequestSearch.sorts` refuses the whole read rather than
354
355
  * falling back, because at a bounded result window another order reaches another subset. The page is a union of
355
356
  * the provider's relationship × state facets, so it is re-ordered as a whole rather than served as concatenated
356
- * per-facet runs. Changing the sort invalidates a threaded cursor (it is part of the cursor's fingerprint,
357
- * alongside the text and the scope); drop the cursor when you change the order.
357
+ * per-facet runs. The order holds within a page, not across pages: each facet advances by its own continuation, so
358
+ * a later page can hold a row that sorts before the last row of an earlier one — sort the accumulated rows when a
359
+ * surface shows several pages as one list. Changing the sort invalidates a threaded cursor (it is part of the
360
+ * cursor's fingerprint, alongside the text and the scope); drop the cursor when you change the order.
358
361
  *
359
362
  * If the provider's result ceiling is reached, the request still succeeds and carries a warning omission with
360
363
  * `totalCount`, `limit`, `sort`, and `recovery: 'none'`. `totalCount` is the largest provider-reported
361
364
  * pre-ceiling facet count, not the reachable or returned row count; this mirrors the per-search ceiling's own
362
365
  * unit.
363
366
  *
367
+ * **Azure DevOps Server** differs in how it gets there, because its pull request API can't filter by text, date or
368
+ * draft: the first page drains every facet (up to 1,000 rows each) and later pages are served from that drain,
369
+ * so a first page can cost many requests and a continuation none. A continuation whose drain expired is refused;
370
+ * restart without the cursor. `itemsPerPage` sizes the whole page, not a
371
+ * facet. Past a facet's drain bound the page is `truncated` with no `totalCount`. Scope names with spaces or
372
+ * quotes are accepted there, since they reach the provider as encoded URL segments. See
373
+ * `docs/integrations.md` §9.
374
+ *
364
375
  * Check `getSupportedFilters().pullRequestSearch` before calling. A provider that reports no relationships
365
376
  * refuses the read rather than returning a page that never honored the criteria or scope.
366
377
  */
@@ -378,7 +389,8 @@ export interface IntegrationManager {
378
389
  *
379
390
  * Held to a STRICTER rule than the free-form `criteria.text`, which is sanitized: a scope name carrying a
380
391
  * quote, or an inner space or control character, is REFUSED (warning + `fetchFailed`), and the refusal
381
- * names the value. Leading and trailing whitespace and control characters are stripped and accepted,
392
+ * names the value — except on a provider whose scope names never enter a query string (Azure DevOps
393
+ * Server), where only blank names and control characters are. Leading and trailing whitespace and control characters are stripped and accepted,
382
394
  * since removing them cannot change which scope the query names. Sanitizing a scope would answer the wrong question — the sanitized value may name a real but
383
395
  * DIFFERENT organization, whose result looks entirely normal. Pass the name exactly as the provider spells
384
396
  * it; `''` means "no org supplied" and falls through to the other scopes.
@@ -394,7 +406,8 @@ export interface IntegrationManager {
394
406
  * 2-state search returns up to `6 × itemsPerPage` items before deduplication, and fewer than that where
395
407
  * the facets overlap. Deduplication does NOT bring the page back to this size: it removes only the rows the
396
408
  * facets share. `page.itemsPerPage` reports what actually came back, so size the UI off that rather than
397
- * off this. A provider may also cap it below what is asked for.
409
+ * off this. A provider may also cap it below what is asked for. Azure DevOps Server is the exception: it
410
+ * serves one merged page, so there this is the page size (at most 100).
398
411
  */
399
412
  itemsPerPage?: number;
400
413
  forceSync?: boolean;
@@ -497,7 +510,15 @@ export interface IntegrationManager {
497
510
  * matched), `limit` (how many are reachable) and `sort` (the order that window was selected under) with
498
511
  * `recovery: 'none'`, so a consumer can say "19.240 matched, showing the 1.000 most recent" — naming the
499
512
  * order, since which 1.000 are reachable depends on it — and know not to offer a "load more". It never falls
500
- * back to a per-repository recovery walk.
513
+ * back to a per-repository recovery walk. A provider that can prove the ceiling was reached without counting
514
+ * past it (Azure DevOps Server, at 20,000) omits `totalCount`, so render the limit alone. An Azure DevOps Server
515
+ * that refuses the match set even with the bounded query fails the page instead (warning + `fetchFailed`),
516
+ * while its count still reports `exceedsProviderLimit`; narrow the search.
517
+ *
518
+ * On **Azure DevOps Server** every relationship is one WIQL query over one collection (`org`, or the
519
+ * repositories' collection), so `itemsPerPage` sizes the whole page. Continuations page through the ids the
520
+ * first page queried while the pagination keeps reading; one whose ids have expired is refused, and the read
521
+ * must restart without the cursor. See `docs/integrations.md` §9.
501
522
  *
502
523
  * Check `getSupportedFilters().issueSearch` first: a provider with no filtered issue search reports empty
503
524
  * relationships (and this read refuses), and a criterion or sort key it can't express refuses the whole read
@@ -517,7 +538,8 @@ export interface IntegrationManager {
517
538
  *
518
539
  * Held to a STRICTER rule than the free-form criteria, which are sanitized: a scope name carrying a quote,
519
540
  * or an inner space or control character, is REFUSED (warning + `fetchFailed`), and the refusal names the
520
- * value. Leading and trailing whitespace and control characters are stripped and accepted, since removing
541
+ * value — except on Azure DevOps Server, whose scope names never enter a query string, where only blank
542
+ * names and control characters are. Leading and trailing whitespace and control characters are stripped and accepted, since removing
521
543
  * them cannot change which scope the query names.
522
544
  * The sanitized value may name a real but DIFFERENT org, whose result looks entirely normal. `''` means
523
545
  * "no org supplied" and falls through to the other scopes.
@@ -560,8 +582,8 @@ export interface IntegrationManager {
560
582
  *
561
583
  * `count: undefined` means the provider didn't report one — NOT zero, which is a real answer. Render the
562
584
  * difference: showing an unknown count as 0 tells the user a filter matches nothing when it may match
563
- * thousands. A provider that can't count at all (only GitHub/GHE can today) refuses the probe outright rather
564
- * than returning fabricated numbers.
585
+ * thousands. A provider that can't count at all (only GitHub/GHE and Azure DevOps Server can today) refuses the
586
+ * probe outright rather than returning fabricated numbers.
565
587
  */
566
588
  countIssues(options: {
567
589
  providerId: IntegrationIds;
@@ -601,31 +623,54 @@ export interface IntegrationManager {
601
623
  * {@link getIssuesBatch}, which cannot serve one.
602
624
  *
603
625
  * `issue: undefined` is a proven absence and may be cached. A failed read returns no item and sets
604
- * `fetchFailed`. `resourceId` is required and trusted; Jira also requires the resource's site URL so the result
605
- * retains its browser link. The read performs no resource discovery.
626
+ * `fetchFailed`. `resourceId` is required and trusted; Jira Cloud also requires the resource's site URL so the
627
+ * result retains its browser link. The read performs no resource discovery.
628
+ *
629
+ * Jira (Cloud and Data Center) and Linear only. Trello refuses: its single-issue read falls back to a capped
630
+ * board scan for a numeric identifier, so it cannot prove absence.
606
631
  *
607
- * Jira and Linear only. Trello refuses: its single-issue read falls back to a capped board scan for a numeric
608
- * identifier, so it cannot prove absence.
632
+ * Jira Data Center is addressed per host and, unlike the paged reads, this one never falls back to the primary
633
+ * connection: it requires a `domain` or a `connectionId` with a configured host and refuses otherwise, since two
634
+ * self-hosted instances routinely issue the same keys and a cached absence must name the host it was proven on.
609
635
  */
610
636
  getTrackerIssue(options: {
611
637
  providerId: IntegrationIds;
612
- /** Provider resource ID for the Atlassian site or Linear workspace. */
638
+ /**
639
+ * Provider resource ID for the Atlassian site or Linear workspace. For Jira Data Center it is the host, the
640
+ * instance's single resource as `listOrgs` reports it, and must name the host the read resolves to.
641
+ */
613
642
  resourceId: string;
614
- /** Jira site URL from resource discovery. Required for Jira so the result retains a browser link. */
643
+ /**
644
+ * Jira site URL from resource discovery. Required for Jira Cloud so the result retains a browser link; Jira
645
+ * Data Center builds it from the connection's own base URL.
646
+ */
615
647
  resourceUrl?: string;
616
648
  /** The provider's own key, e.g. `ABC-123`. Not a number. */
617
649
  key: string;
618
650
  connectionId?: string;
651
+ /**
652
+ * Self-managed tracker host; see {@link ProviderSweepTarget.domain}. Ignored for the cloud trackers. Required
653
+ * for Jira Data Center unless `connectionId` names a configured host — see above.
654
+ */
655
+ domain?: string;
619
656
  }): Promise<ProviderResult<TrackerIssueResult>>;
620
657
  /**
621
- * How many pull requests match each scope, fetching none of them — the PR twin of {@link countIssues}, behind a
622
- * "this will fetch ~N pull requests" preview and a live count next to an unapplied filter. Same cost model,
623
- * per-scope isolation, `key`-echo, and `count: undefined` ≠ zero rule as the issue count; GitHub/GHE only.
624
- *
625
- * The one PR-specific difference: a scope's `states` are counted as independent searches, so the reported count
626
- * is the LARGEST of them (the same total {@link searchPullRequestsPage} surfaces), not their sum. Several states
627
- * in one scope are therefore fine; only several relationships are refused — one relationship per scope, see
628
- * {@link PullRequestCountScope}.
658
+ * How many pull requests match each scope — the PR twin of {@link countIssues}, behind a "this will fetch ~N pull
659
+ * requests" preview and a live count next to an unapplied filter. Same per-scope isolation, `key`-echo, and
660
+ * `count: undefined` ≠ zero rule as the issue count; GitHub/GHE, Bitbucket Data Center and Azure DevOps Server.
661
+ *
662
+ * The one PR-specific difference: on GitHub/GHE a scope's `states` are counted as independent searches, so the
663
+ * reported count is the LARGEST of them (the same total {@link searchPullRequestsPage} surfaces), not their sum.
664
+ * Several states in one scope are therefore fine; only several relationships are refused — one relationship per
665
+ * scope, see {@link PullRequestCountScope}.
666
+ *
667
+ * Bitbucket Data Center has no count query: it counts by reading each facet's first page, so a count there costs
668
+ * requests rather than none, and one past that page comes back as a floor with `lowerBound: true`. Reading lets it
669
+ * deduplicate the rows, so it counts the exact union of the states AND of several relationships in one scope,
670
+ * matching what the search it previews returns.
671
+ *
672
+ * Azure DevOps Server counts from the same drain its search pages through (reusing one its search read in the
673
+ * last minute), so its count is the union of the states — for disjoint states their sum — and costs requests too.
629
674
  */
630
675
  countPullRequests(options: {
631
676
  providerId: IntegrationIds;
@@ -9,8 +9,8 @@ import { RepositoryMetadata } from "../../../git/models/repositoryMetadata.js";
9
9
  import { PullRequestUrlIdentity } from "../../../git/utils/pullRequest.utils.js";
10
10
  import { ProviderAuthenticationSession } from "../authentication/models.js";
11
11
  import { IntegrationIds } from "../constants.js";
12
- import { MyIssuesForReposOptions, ProviderIssueSearchPage } from "./issueReads.js";
13
- import { ProviderPullRequestSearchPage } from "./pullRequestReads.js";
12
+ import { ProviderPullRequestCount, ProviderPullRequestSearchPage } from "./pullRequestReads.js";
13
+ import { MyIssuesForReposOptions, ProviderIssueSearchPage, ProviderSearchCount } from "./issueReads.js";
14
14
  import { IntegrationBase, IntegrationResult, IntegrationType } from "./integration.js";
15
15
  import { ProviderOrganization } from "../results.js";
16
16
  import { ProviderApiPagedResult, ProviderHierarchyResult, ProviderIssue, ProviderPullRequest, ProviderRepoInput, ProviderReposInput, ProviderRepository } from "../providers/models.js";
@@ -45,6 +45,11 @@ type MyPullRequestsForReposOptions = {
45
45
  };
46
46
  export declare abstract class GitHostIntegration<ID extends IntegrationIds = IntegrationIds, T extends ResourceDescriptor = ResourceDescriptor> extends IntegrationBase<ID> {
47
47
  readonly type: IntegrationType;
48
+ /** The self-managed installation `session` addresses, validated against this integration's host. */
49
+ protected getSelfManagedInstallationUrl(session: ProviderAuthenticationSession): string;
50
+ protected getSelfManagedApiBaseUrl(session: ProviderAuthenticationSession): string;
51
+ /** The base the repository-list reads hand the provider client for a self-managed host. */
52
+ protected getRepositoriesApiBaseUrl(session: ProviderAuthenticationSession, _repos: ProviderReposInput): string;
48
53
  getAccountForEmail(repo: T, email: string, options?: {
49
54
  avatarSize?: number;
50
55
  }): Promise<Account$1 | undefined>;
@@ -86,7 +91,7 @@ export declare abstract class GitHostIntegration<ID extends IntegrationIds = Int
86
91
  protected abstract getProviderRepositoryMetadata(session: ProviderAuthenticationSession, repo: T, cancellation?: AbortSignal): Promise<RepositoryMetadata | undefined>;
87
92
  /**
88
93
  * Whether this git host implements generic org discovery. False for providers that register no
89
- * {@link getProviderOrganizationsForUser} hook (e.g. Bitbucket Data Center) — the facade uses this to
94
+ * {@link getProviderOrganizationsForUser} hook — the facade uses this to
90
95
  * report `unsupported` instead of a silent empty list, which is indistinguishable from "has no orgs".
91
96
  */
92
97
  get supportsOrganizationDiscovery(): boolean;
@@ -366,25 +371,26 @@ export declare abstract class GitHostIntegration<ID extends IntegrationIds = Int
366
371
  * at all. Recovers thrown errors into `{ error }` like the reads around it.
367
372
  *
368
373
  * Counts come back POSITIONALLY — one per input scope, in order — because a caller's key must never reach the
369
- * provider query. `undefined` in a slot means the provider didn't report a count for it, never zero matches.
374
+ * provider query. See {@link ProviderSearchCount} for what each slot can hold; `undefined` means the provider
375
+ * didn't report a count for it, never zero matches.
370
376
  */
371
377
  countIssuesResult(scopes: readonly {
372
378
  repos?: ProviderRepoInput[];
373
379
  org?: string;
374
380
  criteria?: IssueSearchCriteria;
375
- }[], cancellation?: AbortSignal, connectionId?: string): Promise<IntegrationResult<(number | undefined)[] | undefined>>;
381
+ }[], cancellation?: AbortSignal, connectionId?: string): Promise<IntegrationResult<ProviderSearchCount[] | undefined>>;
376
382
  /**
377
383
  * OPTIONAL: only a provider that can answer "how many match" WITHOUT fetching the matches implements this.
378
- * GitHub's search reports `issueCount` on a zero-node selection; GitLab's REST exposes a total on some
379
- * endpoints but not for a search-shaped query, and Azure has no equivalent. A provider that can't answer
380
- * doesn't implement it and the facade refuses the probe, so a consumer hides its count rather than being shown
381
- * a fabricated one.
384
+ * GitHub's search reports `issueCount` on a zero-node selection, and Azure DevOps Server's WIQL returns the
385
+ * matching ids without their details; GitLab's REST exposes a total on some endpoints but not for a
386
+ * search-shaped query. A provider that can't answer doesn't implement it and the facade refuses the probe, so a
387
+ * consumer hides its count rather than being shown a fabricated one.
382
388
  */
383
389
  protected countProviderIssues?(session: ProviderAuthenticationSession, scopes: readonly {
384
390
  repos?: ProviderRepoInput[];
385
391
  org?: string;
386
392
  criteria?: IssueSearchCriteria;
387
- }[], cancellation?: AbortSignal): Promise<(number | undefined)[] | undefined>;
393
+ }[], cancellation?: AbortSignal): Promise<ProviderSearchCount[] | undefined>;
388
394
  /**
389
395
  * Result-returning wrapper for the BATCH issue read: resolves several `(owner, repo, number)` coordinates in
390
396
  * one request. Recovers thrown errors into `{ error }` like the reads around it.
@@ -416,18 +422,28 @@ export declare abstract class GitHostIntegration<ID extends IntegrationIds = Int
416
422
  repo: string;
417
423
  number: number;
418
424
  }[], cancellation?: AbortSignal): Promise<(IssueShape | undefined)[] | undefined>;
419
- /** The PR twin of {@link countIssuesResult}: counts each scope's pull requests, transferring none. */
425
+ /**
426
+ * The PR twin of {@link countIssuesResult}: counts each scope's pull requests, transferring none where the
427
+ * provider has a count query. Each slot is a {@link ProviderPullRequestCount}, so a provider that can only count
428
+ * by reading (Bitbucket Data Center) can mark a figure it stopped short of as a floor, or an `Error` refusing only
429
+ * that scope (Azure DevOps Server).
430
+ */
420
431
  countPullRequestsResult(scopes: readonly {
421
432
  repos?: ProviderRepoInput[];
422
433
  org?: string;
423
434
  criteria?: PullRequestSearchCriteria;
424
- }[], cancellation?: AbortSignal, connectionId?: string): Promise<IntegrationResult<(number | undefined)[] | undefined>>;
425
- /** OPTIONAL, like {@link countProviderIssues}: only a provider that can count without fetching implements it. */
435
+ }[], cancellation?: AbortSignal, connectionId?: string): Promise<IntegrationResult<(ProviderPullRequestCount | Error)[] | undefined>>;
436
+ /**
437
+ * OPTIONAL, like {@link countProviderIssues}: only a provider that can answer "how many match" implements it —
438
+ * GitHub/GHE with a zero-node search, Bitbucket Data Center by reading within a budget and reporting a floor
439
+ * past it, Azure DevOps Server from the drain its search reads. Must agree with
440
+ * `ProviderMetadata.supportedPullRequestSearch`, which the facade validates against.
441
+ */
426
442
  protected countProviderPullRequests?(session: ProviderAuthenticationSession, scopes: readonly {
427
443
  repos?: ProviderRepoInput[];
428
444
  org?: string;
429
445
  criteria?: PullRequestSearchCriteria;
430
- }[], cancellation?: AbortSignal): Promise<(number | undefined)[] | undefined>;
446
+ }[], cancellation?: AbortSignal): Promise<(ProviderPullRequestCount | Error)[] | undefined>;
431
447
  getPullRequestIdentityFromMaybeUrl(search: string): PullRequestUrlIdentity | undefined;
432
448
  protected getProviderPullRequestIdentityFromMaybeUrl?(search: string): PullRequestUrlIdentity | undefined;
433
449
  }
@@ -12,25 +12,25 @@ import { throwIfCallerContractError, toCollectionFailureError, toCollectionScope
12
12
  import "../constants.js";
13
13
  import { toTokenWithInfo } from "../authentication/models.js";
14
14
  import "../providerFilters.js";
15
- import { toIssueShape, toProviderIssueStates, toProviderPullRequestStates } from "../providers/models.js";
16
- import { baseUrlFromDomain } from "../utils/domain.utils.js";
17
15
  import { mergeCollectionMetadata, throwIfAllSettledFailed } from "../providers/utils/providerPaging.js";
16
+ import { areDomainsOnSameHost } from "../utils/domain.utils.js";
17
+ import { getSelfManagedBaseUrl, isGitSelfManagedHostIntegrationId } from "../utils/integration.utils.js";
18
+ import { toIssueShape, toProviderIssueStates, toProviderPullRequestStates } from "../providers/models.js";
18
19
  import { IntegrationBase } from "./integration.js";
19
20
  //#region ../plus/integrations/src/models/gitHostIntegration.ts
21
+ /**
22
+ * Whether an SDK read for `providerId` must send the session's secret as an HTTP Basic credential.
23
+ *
24
+ * Azure DevOps authenticates a PAT only as Basic — Azure DevOps Server refuses one sent as a bearer token outright —
25
+ * and the Azure integration's own reads already pass it that way (`AzureDevOpsIntegrationBase.getApiOptions`). The
26
+ * reads here are shared by every host, so they have to ask; every other provider keeps the SDK's default.
27
+ */
28
+ function sendsBasicCredential(providerId, session) {
29
+ return isAzureDevOpsProvider(providerId) ? session.type !== "oauth" : void 0;
30
+ }
20
31
  function isAzureDevOpsProvider(providerId) {
21
32
  return providerId === "azureDevOps" || providerId === "azure-devops-server";
22
33
  }
23
- function getSelfManagedApiBaseUrl(providerId, domain, protocol) {
24
- const baseUrl = baseUrlFromDomain(domain, protocol);
25
- if (baseUrl == null) return void 0;
26
- switch (providerId) {
27
- case "cloud-github-enterprise": return `${baseUrl.replace(/\/api(?:\/v\d+)?$/, "")}/api/v3`;
28
- case "cloud-gitlab-self-hosted": return baseUrl.replace(/\/api(?:\/v\d+)?$/, "");
29
- case "bitbucket-server": return `${baseUrl.replace(/\/rest\/api\/1\.0$/, "")}/rest/api/1.0`;
30
- case "azure-devops-server": return baseUrl;
31
- default: return;
32
- }
33
- }
34
34
  /**
35
35
  * Rejects a read the provider can't serve as asked: logs the reason and returns it as the `{ error }` half of an
36
36
  * {@link IntegrationResult}, timed from `start`.
@@ -58,6 +58,24 @@ const summaryPullRequestFields = {
58
58
  };
59
59
  var GitHostIntegration = class extends IntegrationBase {
60
60
  type = "git";
61
+ /** The self-managed installation `session` addresses, validated against this integration's host. */
62
+ getSelfManagedInstallationUrl(session) {
63
+ const baseUrl = getSelfManagedBaseUrl(this.id, session.baseUrl ?? (session.domain || this.domain), session.protocol);
64
+ if (baseUrl == null || !areDomainsOnSameHost(baseUrl, this.domain)) throw new Error("Invalid self-managed integration base URL");
65
+ return baseUrl;
66
+ }
67
+ getSelfManagedApiBaseUrl(session) {
68
+ const baseUrl = this.getSelfManagedInstallationUrl(session);
69
+ switch (this.id) {
70
+ case "cloud-github-enterprise": return `${baseUrl}/api/v3`;
71
+ case "bitbucket-server": return `${baseUrl}/rest/api/1.0`;
72
+ default: return baseUrl;
73
+ }
74
+ }
75
+ /** The base the repository-list reads hand the provider client for a self-managed host. */
76
+ getRepositoriesApiBaseUrl(session, _repos) {
77
+ return this.getSelfManagedApiBaseUrl(session);
78
+ }
61
79
  async getAccountForEmail(repo, email, options) {
62
80
  const scope = getScopedLogger();
63
81
  if (!(this.maybeConnected ?? await this.isConnected())) return void 0;
@@ -138,7 +156,7 @@ var GitHostIntegration = class extends IntegrationBase {
138
156
  }
139
157
  /**
140
158
  * Whether this git host implements generic org discovery. False for providers that register no
141
- * {@link getProviderOrganizationsForUser} hook (e.g. Bitbucket Data Center) — the facade uses this to
159
+ * {@link getProviderOrganizationsForUser} hook — the facade uses this to
142
160
  * report `unsupported` instead of a silent empty list, which is indistinguishable from "has no orgs".
143
161
  */
144
162
  get supportsOrganizationDiscovery() {
@@ -192,7 +210,12 @@ var GitHostIntegration = class extends IntegrationBase {
192
210
  const start = performance.now();
193
211
  try {
194
212
  const result = await this.getProviderOrganizationsForUser?.(session);
195
- this.resetRequestExceptionCount("getOrganizationsForUser");
213
+ const authFailure = result?.metadata?.failures?.find((failure) => failure.kind === "authentication" && failure.scope != null && failure.scope.providerId === this.id && Object.keys(failure.scope).length === 1);
214
+ if (authFailure != null) this.handleProviderException("getOrganizationsForUser", toCollectionFailureError(authFailure, toTokenWithInfo(this.id, session)), {
215
+ scope,
216
+ connectionId
217
+ });
218
+ else this.resetRequestExceptionCount("getOrganizationsForUser");
196
219
  return {
197
220
  value: result,
198
221
  duration: performance.now() - start
@@ -386,7 +409,10 @@ var GitHostIntegration = class extends IntegrationBase {
386
409
  let pending = this._filterAccounts.get(key);
387
410
  if (pending == null) {
388
411
  const tokenWithInfo = toTokenWithInfo(this.authProvider.id, session);
389
- pending = (organization != null ? api.getCurrentUserForInstance(tokenWithInfo, organization, { baseUrl: customUrl }) : api.getCurrentUser(tokenWithInfo, { baseUrl: customUrl })).catch((ex) => {
412
+ pending = (organization != null ? api.getCurrentUserForInstance(tokenWithInfo, organization, {
413
+ baseUrl: customUrl,
414
+ isPAT: sendsBasicCredential(this.id, session)
415
+ }) : api.getCurrentUser(tokenWithInfo, { baseUrl: customUrl })).catch((ex) => {
390
416
  this._filterAccounts.delete(key);
391
417
  throw ex;
392
418
  });
@@ -410,9 +436,10 @@ var GitHostIntegration = class extends IntegrationBase {
410
436
  const session = await this.resolveReadSession(connectionId, scope);
411
437
  if (session == null) return void 0;
412
438
  const start = performance.now();
413
- const customUrl = options?.customUrl ?? getSelfManagedApiBaseUrl(providerId, session.domain || this.domain, session.protocol);
439
+ let customUrl;
414
440
  let api;
415
441
  try {
442
+ customUrl = options?.customUrl ?? (isGitSelfManagedHostIntegrationId(providerId) ? this.getRepositoriesApiBaseUrl(session, reposOrRepoIds) : void 0);
416
443
  api = await this.getProvidersApi();
417
444
  } catch (ex) {
418
445
  this.handleProviderException("getIssuesForRepos", ex, {
@@ -481,6 +508,7 @@ var GitHostIntegration = class extends IntegrationBase {
481
508
  ...getIssuesOptions,
482
509
  cursor: projectInput.cursor,
483
510
  baseUrl: customUrl,
511
+ isPAT: sendsBasicCredential(providerId, session),
484
512
  page: projectInput.cursor == null ? options?.page : void 0,
485
513
  pageSize: options?.pageSize,
486
514
  states,
@@ -653,6 +681,7 @@ var GitHostIntegration = class extends IntegrationBase {
653
681
  ...getIssuesOptions,
654
682
  cursor: options?.cursor,
655
683
  baseUrl: customUrl,
684
+ isPAT: sendsBasicCredential(providerId, session),
656
685
  page: options?.page,
657
686
  pageSize: options?.pageSize,
658
687
  states,
@@ -732,9 +761,10 @@ var GitHostIntegration = class extends IntegrationBase {
732
761
  const session = await this.resolveReadSession(connectionId, scope);
733
762
  if (session == null) return void 0;
734
763
  const start = performance.now();
735
- const customUrl = options?.customUrl ?? getSelfManagedApiBaseUrl(providerId, session.domain || this.domain, session.protocol);
764
+ let customUrl;
736
765
  let api;
737
766
  try {
767
+ customUrl = options?.customUrl ?? (isGitSelfManagedHostIntegrationId(providerId) ? this.getRepositoriesApiBaseUrl(session, reposOrRepoIds) : void 0);
738
768
  api = await this.getProvidersApi();
739
769
  } catch (ex) {
740
770
  this.handleProviderException("getPullRequestsForRepos", ex, {
@@ -840,7 +870,8 @@ var GitHostIntegration = class extends IntegrationBase {
840
870
  results: await api.getPullRequestsForRepo(toTokenWithInfo(providerId, session), repoInput.repo, {
841
871
  ...getPullRequestsOptions,
842
872
  cursor: repoInput.cursor,
843
- baseUrl: customUrl,
873
+ isPAT: sendsBasicCredential(providerId, session),
874
+ baseUrl: options?.customUrl == null && isGitSelfManagedHostIntegrationId(providerId) ? this.getRepositoriesApiBaseUrl(session, [repoInput.repo]) : customUrl,
844
875
  page: repoInput.cursor == null ? options?.page : void 0,
845
876
  pageSize: options?.pageSize,
846
877
  states,
@@ -904,6 +935,7 @@ var GitHostIntegration = class extends IntegrationBase {
904
935
  const result = await api.getPullRequestsForRepos(toTokenWithInfo(providerId, session), reposOrRepoIds, {
905
936
  ...getPullRequestsOptions,
906
937
  cursor: options?.cursor,
938
+ isPAT: sendsBasicCredential(providerId, session),
907
939
  baseUrl: customUrl,
908
940
  page: options?.page,
909
941
  pageSize: options?.pageSize,
@@ -1082,7 +1114,8 @@ var GitHostIntegration = class extends IntegrationBase {
1082
1114
  * at all. Recovers thrown errors into `{ error }` like the reads around it.
1083
1115
  *
1084
1116
  * Counts come back POSITIONALLY — one per input scope, in order — because a caller's key must never reach the
1085
- * provider query. `undefined` in a slot means the provider didn't report a count for it, never zero matches.
1117
+ * provider query. See {@link ProviderSearchCount} for what each slot can hold; `undefined` means the provider
1118
+ * didn't report a count for it, never zero matches.
1086
1119
  */
1087
1120
  async countIssuesResult(scopes, cancellation, connectionId) {
1088
1121
  const scope = getScopedLogger();
@@ -1144,7 +1177,12 @@ var GitHostIntegration = class extends IntegrationBase {
1144
1177
  };
1145
1178
  }
1146
1179
  }
1147
- /** The PR twin of {@link countIssuesResult}: counts each scope's pull requests, transferring none. */
1180
+ /**
1181
+ * The PR twin of {@link countIssuesResult}: counts each scope's pull requests, transferring none where the
1182
+ * provider has a count query. Each slot is a {@link ProviderPullRequestCount}, so a provider that can only count
1183
+ * by reading (Bitbucket Data Center) can mark a figure it stopped short of as a floor, or an `Error` refusing only
1184
+ * that scope (Azure DevOps Server).
1185
+ */
1148
1186
  async countPullRequestsResult(scopes, cancellation, connectionId) {
1149
1187
  const scope = getScopedLogger();
1150
1188
  const session = await this.resolveReadSession(connectionId, scope);