@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
@@ -5,8 +5,9 @@ import { toCollectionScopeFailure } from "../collectionMetadata.js";
5
5
  import "../constants.js";
6
6
  import { toTokenWithInfo } from "../authentication/models.js";
7
7
  import "../providerFilters.js";
8
- import { fromProviderIssue, fromProviderPullRequest, getProviderPullRequestIdentity, providerPullRequestMatchesSearch, providersMetadata, toProviderPullRequestStates } from "./models.js";
9
8
  import { collectProviderPagedResult, flatSettledResultsOrThrow, mergeCollectionMetadata } from "./utils/providerPaging.js";
9
+ import { decodePathSegment } from "../utils/domain.utils.js";
10
+ import { fromProviderIssue, fromProviderPullRequest, getProviderPullRequestIdentity, providerPullRequestMatchesSearch, providersMetadata, toProviderPullRequestStates } from "./models.js";
10
11
  import { GitHostIntegration } from "../models/gitHostIntegration.js";
11
12
  //#region ../plus/integrations/src/providers/azureDevOps.ts
12
13
  function getAzureRepositoryIdentity(repo) {
@@ -18,10 +19,33 @@ function getAzureRepositoryIdentity(repo) {
18
19
  };
19
20
  }
20
21
  function getAzureRepositoryApiBaseUrl(baseUrl, repo) {
21
- if (repo.virtualDirectory == null) return baseUrl;
22
- const segments = repo.virtualDirectory.split("/");
23
- if (segments.some((segment) => !segment || segment === "." || segment === "..")) throw new Error(`Invalid Azure virtual directory '${repo.virtualDirectory}'.`);
24
- return `${baseUrl.replace(/\/+$/, "")}/${segments.map(encodeURIComponent).join("/")}`;
22
+ const directory = repo.virtualDirectory?.split("/");
23
+ if (directory?.some((segment) => !segment || segment === "." || segment === "..")) throw new Error(`Invalid Azure virtual directory '${repo.virtualDirectory}'.`);
24
+ const url = new URL(baseUrl);
25
+ const toUrl = (segments) => `${url.protocol}//${url.host}${segments.map((s) => `/${s}`).join("")}`;
26
+ const configured = url.pathname.split("/").filter(Boolean);
27
+ if (!configured.length) return toUrl(directory?.map(encodeURIComponent) ?? []);
28
+ const namesCollection = sameAzurePathSegment(configured.at(-1), repo.owner);
29
+ const installation = namesCollection ? configured.slice(0, -1) : configured;
30
+ if (directory == null) return toUrl(installation);
31
+ for (const candidate of namesCollection ? [configured, installation] : [configured]) if (candidate.length === directory.length && candidate.every((s, i) => sameAzurePathSegment(s, directory[i]))) return toUrl(candidate);
32
+ throw new Error("Azure repository virtual directory does not match the configured installation");
33
+ }
34
+ function sameAzurePathSegment(encoded, value) {
35
+ return decodePathSegment(encoded).toLowerCase() === value.toLowerCase();
36
+ }
37
+ /**
38
+ * Whether two collection or project names name the same scope. Azure DevOps treats them case-insensitively, so a
39
+ * name typed by hand must still select the collection or project discovery reported.
40
+ */
41
+ function sameAzureName(a, b) {
42
+ return a.toLowerCase() === b.toLowerCase();
43
+ }
44
+ /** The distinct names in `names`, compared as Azure compares them, keeping the first spelling of each. */
45
+ function uniqueAzureNames(names) {
46
+ const unique = [];
47
+ for (const name of names) if (!unique.some((n) => sameAzureName(n, name))) unique.push(name);
48
+ return unique;
25
49
  }
26
50
  /**
27
51
  * Matches an org/project descriptor against a caller-supplied name, mirroring the facade's own
@@ -39,15 +63,51 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
39
63
  options: { isPAT: usePat }
40
64
  };
41
65
  }
66
+ /**
67
+ * The options for a request addressed below one collection (an Azure DevOps Services organization).
68
+ *
69
+ * The same as {@link getApiOptions} unless a self-managed address already names the collection, which requests
70
+ * append themselves: see the server override, which keeps it from being applied twice.
71
+ */
72
+ getCollectionApiOptions(session, _collection) {
73
+ return this.getApiOptions(session).options;
74
+ }
75
+ /** The base a request or link below `collection` appends the collection to; see {@link getCollectionApiOptions}. */
76
+ collectionApiBaseUrl(session, _collection) {
77
+ return this.apiBaseUrlFor(session);
78
+ }
79
+ /** Reads the organizations (Azure DevOps Server: the collections) the account can see. */
80
+ async requestResourcesForUser(session, userId) {
81
+ const api = await this.getProvidersApi();
82
+ const { tokenWithInfo, options } = this.getApiOptions(session);
83
+ return api.getAzureResourcesForUser(tokenWithInfo, userId, options);
84
+ }
85
+ /**
86
+ * The identity id a pull-request creator/reviewer filter must carry for `collection`.
87
+ *
88
+ * On Azure DevOps Services that is the account id, one per user. The server override resolves it per collection,
89
+ * since Azure DevOps Server gives one person a different id in each collection than at the server level.
90
+ */
91
+ async getFilterUserId(session, _collection) {
92
+ return (await this.getProviderCurrentAccount(session))?.id;
93
+ }
94
+ /**
95
+ * What the discovery caches (account, organizations, projects, and their stored copies) are keyed by. Azure DevOps
96
+ * Services has a single address, so the credential alone identifies what it discovers; the server override adds
97
+ * the installation address.
98
+ */
99
+ discoveryKey(session) {
100
+ return session.accessToken;
101
+ }
42
102
  _accounts;
43
103
  async getProviderCurrentAccount(session) {
44
- const { accessToken } = session;
104
+ const key = this.discoveryKey(session);
45
105
  this._accounts ??= /* @__PURE__ */ new Map();
46
- if (this._accounts.get(accessToken) == null) {
106
+ if (this._accounts.get(key) == null) {
47
107
  const user = await this._requestForCurrentUser(session);
48
- this._accounts.set(accessToken, user);
108
+ this._accounts.set(key, user);
49
109
  }
50
- return this._accounts.get(accessToken);
110
+ return this._accounts.get(key);
51
111
  }
52
112
  async _requestForCurrentUser(session) {
53
113
  const api = await this.getProvidersApi();
@@ -65,19 +125,17 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
65
125
  _organizations;
66
126
  async getProviderResourcesForUser(session, force = false) {
67
127
  this._organizations ??= /* @__PURE__ */ new Map();
68
- const { accessToken } = session;
69
- if (this._organizations.get(accessToken) == null || force) {
70
- const api = await this.getProvidersApi();
128
+ const key = this.discoveryKey(session);
129
+ if (this._organizations.get(key) == null || force) {
71
130
  const account = await this.getProviderCurrentAccount(session);
72
131
  if (account?.id == null) return void 0;
73
- const { tokenWithInfo, options } = this.getApiOptions(session);
74
- const resources = await api.getAzureResourcesForUser(tokenWithInfo, account.id, options);
75
- this._organizations.set(accessToken, resources != null ? resources.map((r) => ({
132
+ const resources = await this.requestResourcesForUser(session, account.id);
133
+ this._organizations.set(key, resources != null ? resources.map((r) => ({
76
134
  ...r,
77
135
  key: r.id
78
136
  })) : void 0);
79
137
  }
80
- return this._organizations.get(accessToken);
138
+ return this._organizations.get(key);
81
139
  }
82
140
  _projects;
83
141
  /**
@@ -89,22 +147,22 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
89
147
  */
90
148
  async getProviderProjectsForResources(session, resources, force = false, failures) {
91
149
  this._projects ??= /* @__PURE__ */ new Map();
92
- const { accessToken } = session;
150
+ const discoveryKey = this.discoveryKey(session);
93
151
  let resourcesWithoutProjects = [];
94
152
  if (force) resourcesWithoutProjects = resources;
95
153
  else for (const resource of resources) {
96
- const resourceKey = `${accessToken}:${resource.id}`;
154
+ const resourceKey = `${discoveryKey}:${resource.id}`;
97
155
  if (this._projects.get(resourceKey) == null) resourcesWithoutProjects.push(resource);
98
156
  }
99
157
  const allProjects = [];
100
158
  let resultMetadata;
101
159
  if (resourcesWithoutProjects.length > 0) {
102
160
  const api = await this.getProvidersApi();
103
- const { tokenWithInfo, options } = this.getApiOptions(session);
161
+ const { tokenWithInfo } = this.getApiOptions(session);
104
162
  (await Promise.allSettled(resourcesWithoutProjects.map(async (resource) => ({
105
163
  resource,
106
164
  result: await collectProviderPagedResult((cursor) => api.getAzureProjectsForResource(tokenWithInfo, resource.name, {
107
- ...options,
165
+ ...this.getCollectionApiOptions(session, resource.name),
108
166
  cursor
109
167
  }), 20, {
110
168
  providerId: this.id,
@@ -149,11 +207,11 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
149
207
  allProjects.push(...projects);
150
208
  return;
151
209
  }
152
- this._projects.set(`${accessToken}:${resource.id}`, projects);
210
+ this._projects.set(`${discoveryKey}:${resource.id}`, projects);
153
211
  });
154
212
  }
155
213
  const cachedProjects = resources.reduce((projects, resource) => {
156
- const resourceProjects = this._projects.get(`${accessToken}:${resource.id}`);
214
+ const resourceProjects = this._projects.get(`${discoveryKey}:${resource.id}`);
157
215
  if (resourceProjects != null) projects.push(...resourceProjects);
158
216
  return projects;
159
217
  }, []);
@@ -173,9 +231,9 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
173
231
  const descriptors = /* @__PURE__ */ new Map();
174
232
  if (projects.length === 0) return descriptors;
175
233
  const api = await this.getProvidersApi();
176
- const { tokenWithInfo, options } = this.getApiOptions(session);
234
+ const { tokenWithInfo } = this.getApiOptions(session);
177
235
  await Promise.all(projects.map(async (project) => {
178
- const repos = (await api.getReposForAzureProject(tokenWithInfo, project.resourceName, project.name, options))?.values;
236
+ const repos = (await api.getReposForAzureProject(tokenWithInfo, project.resourceName, project.name, this.getCollectionApiOptions(session, project.resourceName)))?.values;
179
237
  if (repos != null && repos.length > 0) descriptors.set(project.id, repos.map((r) => ({
180
238
  id: r.id,
181
239
  nodeId: r.graphQLId ?? void 0,
@@ -197,7 +255,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
197
255
  id: o.id,
198
256
  providerId: this.id,
199
257
  name: o.name,
200
- url: `${this.apiBaseUrl}/${o.name}`
258
+ url: `${this.collectionApiBaseUrl(session, o.name)}/${o.name}`
201
259
  })) };
202
260
  }
203
261
  async getProviderProjectsForOrg(session, org) {
@@ -220,7 +278,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
220
278
  providerId: this.id,
221
279
  name: p.name,
222
280
  org: p.resourceName,
223
- url: `${this.apiBaseUrl}/${p.resourceName}/${p.name}`
281
+ url: `${this.collectionApiBaseUrl(session, p.resourceName)}/${p.resourceName}/${p.name}`
224
282
  })),
225
283
  ...projects.metadata != null ? { metadata: projects.metadata } : {}
226
284
  };
@@ -234,7 +292,8 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
234
292
  */
235
293
  async getProviderRepositoriesForOrg(session, org, options) {
236
294
  const api = await this.getProvidersApi();
237
- const { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);
295
+ const { tokenWithInfo } = this.getApiOptions(session);
296
+ const apiOptions = this.getCollectionApiOptions(session, org);
238
297
  if (options?.project) return api.getReposForAzureProject(tokenWithInfo, org, options.project, {
239
298
  ...apiOptions,
240
299
  cursor: options.cursor
@@ -276,7 +335,8 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
276
335
  async mergeProviderPullRequest(session, pr, options) {
277
336
  const api = await this.getProvidersApi();
278
337
  if (pr.refs == null || pr.project == null) return false;
279
- const { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);
338
+ const { tokenWithInfo } = this.getApiOptions(session);
339
+ const apiOptions = this.getCollectionApiOptions(session, pr.repository.owner);
280
340
  try {
281
341
  return await api.mergePullRequest(tokenWithInfo, pr, {
282
342
  ...options,
@@ -291,17 +351,17 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
291
351
  this.ctx.hooks?.ui?.onError?.(`${ex.message}. Check branch policies, and ensure you have the necessary permissions to merge the pull request.`);
292
352
  }
293
353
  async getProviderAccountForCommit(session, repo, rev, options) {
294
- return (await this.authenticationService.apis.azure)?.getAccountForCommit(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, rev, getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo), options);
354
+ return (await this.authenticationService.apis.azure)?.getAccountForCommit(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, rev, getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo), options);
295
355
  }
296
356
  async getProviderAccountForEmail(_session, _repo, _email, _options) {
297
357
  return Promise.resolve(void 0);
298
358
  }
299
359
  async getProviderDefaultBranch(session, repo, cancellation) {
300
- return (await this.authenticationService.apis.azure)?.getDefaultBranch(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, { baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo) }, cancellation);
360
+ return (await this.authenticationService.apis.azure)?.getDefaultBranch(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, { baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo) }, cancellation);
301
361
  }
302
362
  async getProviderLinkedIssueOrPullRequest(session, repo, { id }, type) {
303
363
  return (await this.authenticationService.apis.azure)?.getIssueOrPullRequest(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, id, {
304
- baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo),
364
+ baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo),
305
365
  type
306
366
  });
307
367
  }
@@ -313,13 +373,13 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
313
373
  if (projects.values.length === 0) return void 0;
314
374
  const matchingProject = projects.values.find((p) => p.resourceName === project.owner && p.name === project.name);
315
375
  if (matchingProject == null) return void 0;
316
- return (await this.authenticationService.apis.azure)?.getIssue(this, toTokenWithInfo(this.id, session), matchingProject, id, { baseUrl: this.apiBaseUrl });
376
+ return (await this.authenticationService.apis.azure)?.getIssue(this, toTokenWithInfo(this.id, session), matchingProject, id, { baseUrl: this.collectionApiBaseUrl(session, matchingProject.resourceName) });
317
377
  }
318
378
  async getProviderPullRequestForBranch(session, repo, branch, _options) {
319
- return (await this.authenticationService.apis.azure)?.getPullRequestForBranch(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, branch, { baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo) });
379
+ return (await this.authenticationService.apis.azure)?.getPullRequestForBranch(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, branch, { baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo) });
320
380
  }
321
381
  async getProviderPullRequestForCommit(session, repo, rev) {
322
- return (await this.authenticationService.apis.azure)?.getPullRequestForCommit(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, rev, getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo));
382
+ return (await this.authenticationService.apis.azure)?.getPullRequestForCommit(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, rev, getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo));
323
383
  }
324
384
  async getRepoInfo(repo) {
325
385
  const identity = getAzureRepositoryIdentity(repo);
@@ -330,47 +390,53 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
330
390
  const { tokenWithInfo, options } = this.getApiOptions(session);
331
391
  return api.getRepo(tokenWithInfo, identity.resourceName, identity.repositoryName, identity.projectName, {
332
392
  ...options,
333
- baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo)
393
+ baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo)
334
394
  });
335
395
  }
336
396
  async getProviderRepositoryMetadata(session, repo, cancellation) {
337
- return (await this.authenticationService.apis.azure)?.getRepositoryMetadata(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, { baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrl, repo) }, cancellation);
397
+ return (await this.authenticationService.apis.azure)?.getRepositoryMetadata(this, toTokenWithInfo(this.id, session), repo.owner, repo.name, { baseUrl: getAzureRepositoryApiBaseUrl(this.apiBaseUrlFor(session), repo) }, cancellation);
338
398
  }
339
399
  async searchProviderMyPullRequests(session, repos, _cancellation, options) {
340
400
  const api = await this.getProvidersApi();
341
401
  if (repos != null) return;
342
402
  const states = toProviderPullRequestStates(options?.state);
343
- const user = await this.getProviderCurrentAccount(session);
344
- if (user?.id == null) return void 0;
403
+ if ((await this.getProviderCurrentAccount(session))?.id == null) return void 0;
345
404
  const orgs = await this.getProviderResourcesForUser(session);
346
405
  if (orgs == null || orgs.length === 0) return void 0;
347
406
  const projects = await this.getProviderProjectsForResources(session, orgs);
348
407
  if (projects.values.length === 0) return void 0;
349
408
  const repoDescriptors = [...(await this.getRepoDescriptorsForProjects(session, projects.values) ?? /* @__PURE__ */ new Map()).values()].filter((r) => r != null).flat();
350
- const { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);
351
- const projectInputs = projects.values.map((p) => ({
352
- namespace: p.resourceName,
353
- project: p.name
354
- }));
355
- const assignedPrs = (await api.getPullRequestsForAzureProjects(tokenWithInfo, projectInputs, {
356
- ...apiOptions,
357
- assigneeLogins: [user.id],
358
- states
359
- })).values.map((pr) => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects.values));
360
- const authoredPrs = (await api.getPullRequestsForAzureProjects(tokenWithInfo, projectInputs, {
361
- ...apiOptions,
362
- authorLogin: user.id,
363
- states
364
- })).values.map((pr) => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects.values));
365
- const prsById = /* @__PURE__ */ new Map();
366
- for (const pr of authoredPrs) prsById.set(pr.id, pr);
367
- for (const pr of assignedPrs) if (prsById.get(pr.id) == null) prsById.set(pr.id, pr);
368
- return [...prsById.values()];
409
+ const { tokenWithInfo } = this.getApiOptions(session);
410
+ const assignedPrs = [];
411
+ const authoredPrs = [];
412
+ for (const org of uniqueAzureNames(projects.values.map((p) => p.resourceName))) {
413
+ const userId = await this.getFilterUserId(session, org);
414
+ if (userId == null) continue;
415
+ const projectInputs = projects.values.filter((p) => sameAzureName(p.resourceName, org)).map((p) => ({
416
+ namespace: p.resourceName,
417
+ project: p.name
418
+ }));
419
+ const orgOptions = this.getCollectionApiOptions(session, org);
420
+ const reads = [[{ assigneeLogins: [userId] }, assignedPrs], [{ authorLogin: userId }, authoredPrs]];
421
+ for (const [filter, into] of reads) {
422
+ const result = await api.getPullRequestsForAzureProjects(tokenWithInfo, projectInputs, {
423
+ ...orgOptions,
424
+ ...filter,
425
+ states
426
+ });
427
+ into.push(...result.values.map((pr) => this.fromAzureProviderPullRequest(pr, repoDescriptors, projects.values)));
428
+ }
429
+ }
430
+ const prsByIdentity = /* @__PURE__ */ new Map();
431
+ for (const pr of [...authoredPrs, ...assignedPrs]) {
432
+ const identity = `${pr.repository.owner}/${pr.repository.id || pr.repository.repo}#${pr.id}`;
433
+ if (!prsByIdentity.has(identity)) prsByIdentity.set(identity, pr);
434
+ }
435
+ return [...prsByIdentity.values()];
369
436
  }
370
437
  async getProviderMyPullRequestsForUser(session, options) {
371
438
  const api = await this.getProvidersApi();
372
- const user = await this.getProviderCurrentAccount(session);
373
- if (user?.id == null) return void 0;
439
+ if ((await this.getProviderCurrentAccount(session))?.id == null) return void 0;
374
440
  const orgs = await this.getProviderResourcesForUser(session);
375
441
  if (orgs == null || orgs.length === 0) return void 0;
376
442
  const failures = [];
@@ -384,7 +450,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
384
450
  },
385
451
  ...projects.metadata != null ? { metadata: projects.metadata } : {}
386
452
  };
387
- const { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);
453
+ const { tokenWithInfo } = this.getApiOptions(session);
388
454
  const states = toProviderPullRequestStates(options?.state);
389
455
  const maxPagesPerProject = 20;
390
456
  let truncated = projects.metadata != null && projects.metadata.completeness !== "complete";
@@ -394,7 +460,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
394
460
  let page;
395
461
  for (let i = 0; i < maxPagesPerProject; i++) try {
396
462
  const result = await api.getPullRequestsForAzureProject(tokenWithInfo, project, {
397
- ...apiOptions,
463
+ ...this.getCollectionApiOptions(session, project.namespace),
398
464
  ...filter,
399
465
  states,
400
466
  page
@@ -436,6 +502,8 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
436
502
  const requested = options?.filters?.length ? new Set(options.filters) : void 0;
437
503
  const wantAuthored = requested == null || requested.has("author");
438
504
  const wantReviewed = requested == null || requested.has("assignee") || requested.has("review-requested");
505
+ const userIds = /* @__PURE__ */ new Map();
506
+ for (const org of uniqueAzureNames(projects.values.map((p) => p.resourceName))) userIds.set(org.toLowerCase(), await this.getFilterUserId(session, org));
439
507
  const outcomes = await Promise.all(projects.values.flatMap((p) => {
440
508
  const project = {
441
509
  namespace: p.resourceName,
@@ -446,9 +514,15 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
446
514
  resourceId: p.resourceId,
447
515
  projectId: p.name
448
516
  };
517
+ const userId = userIds.get(p.resourceName.toLowerCase());
518
+ if (userId == null) {
519
+ failures.push(toCollectionScopeFailure(scope, /* @__PURE__ */ new Error("The current user could not be resolved here")));
520
+ truncated = true;
521
+ return [];
522
+ }
449
523
  const drains = [];
450
- if (wantAuthored) drains.push(drainProject(project, scope, { authorLogin: user.id }));
451
- if (wantReviewed) drains.push(drainProject(project, scope, { reviewerId: user.id }));
524
+ if (wantAuthored) drains.push(drainProject(project, scope, { authorLogin: userId }));
525
+ if (wantReviewed) drains.push(drainProject(project, scope, { reviewerId: userId }));
452
526
  return drains;
453
527
  }));
454
528
  const prsByIdentity = /* @__PURE__ */ new Map();
@@ -488,7 +562,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
488
562
  const repoInputs = requestedRepos == null ? void 0 : repoDescriptors.filter((r) => requestedRepos.some((repo) => repo.resourceName === r.resourceName && repo.repositoryName === r.name && (repo.projectName == null || repo.projectName === r.projectName)));
489
563
  if (repoInputs?.length === 0) return [];
490
564
  const api = await this.getProvidersApi();
491
- const { tokenWithInfo, options: apiOptions } = this.getApiOptions(session);
565
+ const { tokenWithInfo } = this.getApiOptions(session);
492
566
  const states = toProviderPullRequestStates(options?.include);
493
567
  const searchScopes = repoInputs != null ? repoInputs.flatMap((repo) => repo.projectName == null ? [] : [{
494
568
  project: {
@@ -511,7 +585,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
511
585
  for (let i = 0; i < 20; i++) {
512
586
  if (cancellation?.aborted) throw new CancellationError();
513
587
  const result = await api.getPullRequestsForAzureProject(tokenWithInfo, scope.project, {
514
- ...apiOptions,
588
+ ...this.getCollectionApiOptions(session, scope.project.namespace),
515
589
  page,
516
590
  repo: scope.repo,
517
591
  states
@@ -571,10 +645,10 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
571
645
  values: [],
572
646
  truncated: false
573
647
  };
574
- const { tokenWithInfo, options } = this.getApiOptions(session);
648
+ const { tokenWithInfo } = this.getApiOptions(session);
575
649
  const drain = async (p, filter) => {
576
650
  const result = await collectProviderPagedResult((cursor) => api.getIssuesForAzureProject(tokenWithInfo, p.resourceName, p.name, {
577
- ...options,
651
+ ...this.getCollectionApiOptions(session, p.resourceName),
578
652
  ...filter,
579
653
  cursor,
580
654
  sort: searchOptions?.sort
@@ -623,10 +697,11 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
623
697
  }
624
698
  async providerOnConnect() {
625
699
  if (this._session == null) return;
700
+ const discoveryKey = this.discoveryKey(this._session);
626
701
  const canHydrateStoredProjects = (metadata) => metadata == null || metadata.completeness === "complete";
627
- const storedAccount = this.ctx.storage.get(`azure:${this._session.accessToken}:account`);
628
- const storedOrganizations = this.ctx.storage.get(`azure:${this._session.accessToken}:organizations`);
629
- const storedProjects = this.ctx.storage.get(`azure:${this._session.accessToken}:projects`);
702
+ const storedAccount = this.ctx.storage.get(`azure:${discoveryKey}:account`);
703
+ const storedOrganizations = this.ctx.storage.get(`azure:${discoveryKey}:organizations`);
704
+ const storedProjects = this.ctx.storage.get(`azure:${discoveryKey}:projects`);
630
705
  let account = storedAccount?.data ? {
631
706
  ...storedAccount.data,
632
707
  provider: this
@@ -645,7 +720,7 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
645
720
  account = await this.getProviderCurrentAccount(this._session);
646
721
  if (account != null) {
647
722
  await this.ctx.storage.deleteWithPrefix("azure");
648
- await this.ctx.storage.store(`azure:${this._session.accessToken}:account`, {
723
+ await this.ctx.storage.store(`azure:${discoveryKey}:account`, {
649
724
  v: 1,
650
725
  timestamp: Date.now(),
651
726
  data: {
@@ -659,29 +734,29 @@ var AzureDevOpsIntegrationBase = class extends GitHostIntegration {
659
734
  }
660
735
  }
661
736
  this._accounts ??= /* @__PURE__ */ new Map();
662
- this._accounts.set(this._session.accessToken, account);
737
+ this._accounts.set(discoveryKey, account);
663
738
  if (storedOrganizations == null) {
664
739
  organizations = await this.getProviderResourcesForUser(this._session, true);
665
- await this.ctx.storage.store(`azure:${this._session.accessToken}:organizations`, {
740
+ await this.ctx.storage.store(`azure:${discoveryKey}:organizations`, {
666
741
  v: 1,
667
742
  timestamp: Date.now(),
668
743
  data: organizations
669
744
  });
670
745
  }
671
746
  this._organizations ??= /* @__PURE__ */ new Map();
672
- this._organizations.set(this._session.accessToken, organizations);
747
+ this._organizations.set(discoveryKey, organizations);
673
748
  if (projects == null && organizations?.length) {
674
749
  projects = await this.getProviderProjectsForResources(this._session, organizations);
675
- if (projects != null && canHydrateStoredProjects(projects.metadata)) await this.ctx.storage.store(`azure:${this._session.accessToken}:projects`, {
750
+ if (projects != null && canHydrateStoredProjects(projects.metadata)) await this.ctx.storage.store(`azure:${discoveryKey}:projects`, {
676
751
  v: 2,
677
752
  timestamp: Date.now(),
678
753
  data: projects
679
754
  });
680
- else await this.ctx.storage.delete(`azure:${this._session.accessToken}:projects`);
755
+ else await this.ctx.storage.delete(`azure:${discoveryKey}:projects`);
681
756
  }
682
757
  this._projects ??= /* @__PURE__ */ new Map();
683
758
  if (projects != null && canHydrateStoredProjects(projects.metadata)) for (const project of projects.values) {
684
- const projectKey = `${this._session.accessToken}:${project.resourceId}`;
759
+ const projectKey = `${discoveryKey}:${project.resourceId}`;
685
760
  const projects = this._projects.get(projectKey);
686
761
  if (projects == null) this._projects.set(projectKey, [project]);
687
762
  else if (!projects.some((p) => p.id === project.id)) projects.push(project);
@@ -729,53 +804,11 @@ var AzureDevOpsIntegration = class extends AzureDevOpsIntegrationBase {
729
804
  get domain() {
730
805
  return cloudMetadata.domain;
731
806
  }
732
- get apiBaseUrl() {
807
+ apiBaseUrlFor(_session) {
733
808
  return "https://dev.azure.com";
734
809
  }
735
810
  };
736
- const serverMetadata = providersMetadata["azure-devops-server"];
737
- const serverAuthProvider = Object.freeze({
738
- id: serverMetadata.id,
739
- scopes: serverMetadata.scopes
740
- });
741
- var AzureDevOpsServerIntegration = class extends AzureDevOpsIntegrationBase {
742
- domain;
743
- authProvider = serverAuthProvider;
744
- id = "azure-devops-server";
745
- key;
746
- name = "Azure DevOps Server";
747
- constructor(ctx, authenticationService, getProvidersApi, didChangeConnection, domain) {
748
- super(ctx, authenticationService, getProvidersApi, didChangeConnection);
749
- this.domain = domain;
750
- this.key = `${this.id}:${this.domain}`;
751
- }
752
- get apiBaseUrl() {
753
- return `${this._session?.protocol ?? "https:"}//${this.domain}`;
754
- }
755
- getApiOptions(session, doNotConvertToPat = false) {
756
- const { options, ...rest } = super.getApiOptions(session, doNotConvertToPat);
757
- return {
758
- ...rest,
759
- options: {
760
- ...options,
761
- baseUrl: this.apiBaseUrl
762
- }
763
- };
764
- }
765
- async _requestForCurrentUser(session) {
766
- const azure = await this.authenticationService.apis.azure;
767
- const user = azure ? await azure.getCurrentUserOnServer(this, toTokenWithInfo(this.id, session), this.apiBaseUrl) : void 0;
768
- return user ? {
769
- provider: this,
770
- id: user.id,
771
- name: user.name ?? void 0,
772
- email: user.email ?? void 0,
773
- avatarUrl: user.avatarUrl ?? void 0,
774
- username: user.username ?? void 0
775
- } : void 0;
776
- }
777
- };
778
811
  //#endregion
779
- export { AzureDevOpsIntegration, AzureDevOpsIntegrationBase, AzureDevOpsServerIntegration };
812
+ export { AzureDevOpsIntegration, AzureDevOpsIntegrationBase, getAzureRepositoryApiBaseUrl, getAzureRepositoryIdentity, sameAzureName, uniqueAzureNames };
780
813
 
781
814
  //# sourceMappingURL=azureDevOps.js.map