@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
@@ -114,6 +114,35 @@ export declare class IntegrationService implements Disposable, RepositoryResolut
114
114
  * self-managed host without persisting `ConfiguredIntegrationService` state.
115
115
  */
116
116
  getIntegrationForRead(id: IntegrationIds, connectionId: string | undefined, domain?: string): Promise<Integration | undefined>;
117
+ /**
118
+ * Resolves every integration instance an account-wide read must cover for `id`. A cloud id has one instance;
119
+ * a self-managed id has one per configured host, so the read fans out over all of them rather than `get(id)`
120
+ * silently picking whichever host was cached (or flagged primary) first, which dropped every other GitHub
121
+ * Enterprise server, GitLab instance, Bitbucket Data Center, Azure DevOps Server or Jira Data Center the user
122
+ * is connected to (#5873).
123
+ *
124
+ * Hosts come from every configured connection, local as well as cloud: that is the set `get(id)` already picked
125
+ * its primary from, and a locally configured host is as much the user's as a cloud-synced one. This is where it
126
+ * departs from {@link getSupportedCloudIntegrations}, which only walks cloud connections because it exists to
127
+ * sync them. The two share the per-host resolution and the fallback to whatever instance is already cached when
128
+ * no host is configured. A lookup that throws counts as "not available", like {@link getIntegrationForRead}.
129
+ */
130
+ getIntegrationsForAccountWideRead<T extends IntegrationIds>(id: T): Promise<IntegrationById<T>[]>;
131
+ isConnectedForAccountWideRead(id: IntegrationIds, options?: {
132
+ access?: boolean;
133
+ }): Promise<boolean>;
134
+ /**
135
+ * The open repositories to scope `integration`'s read to, out of the remotes open for its integration id.
136
+ *
137
+ * With a single host for the id (every cloud id, and a self-managed id with one configured host) every remote
138
+ * is that host's, as it was before the read fanned out, so a remote addressed through an SSH alias or a custom
139
+ * domain keeps its repositories scoped. With several hosts a remote belongs to the one configured host it
140
+ * names, compared the way {@link resolveRepository} compares them: exactly for a web remote, by hostname for an
141
+ * SSH/git one, whose parsed domain has lost any port. A remote naming no host, or several (two ports on one
142
+ * machine), is credited to none, like `resolveRepository`'s `host-mismatch`: asking one server about another
143
+ * server's `owner/name` reads a different repository, or nothing, under the right name.
144
+ */
145
+ private getOpenRemotesForHost;
117
146
  resolveDomainForRead(id: IntegrationIds, connectionId: string | undefined, domain: string | undefined): string | undefined;
118
147
  /** The primary configured host for a self-managed provider (first configured when none is flagged primary). */
119
148
  private primaryConfiguredDomain;
@@ -300,6 +329,11 @@ export declare class IntegrationService implements Disposable, RepositoryResolut
300
329
  resourceUrl?: string;
301
330
  key: string;
302
331
  connectionId?: string;
332
+ /**
333
+ * Explicit self-managed host domain. Unlike the paged reads this one does not fall back to the primary
334
+ * connection for a self-managed tracker; see {@link IntegrationManager.getTrackerIssue}.
335
+ */
336
+ domain?: string;
303
337
  }): Promise<ProviderResult<TrackerIssueResult>>;
304
338
  /**
305
339
  * How many pull requests match each scope, without fetching any — the PR twin of {@link countIssues}. See
@@ -1,16 +1,18 @@
1
1
  import { filterMap, flatten } from "../../utils/iterable.js";
2
2
  import { getScopedLogger } from "../../utils/logger.scoped.js";
3
+ import { mapSettledBounded } from "../../utils/promise.js";
3
4
  import { debug, trace } from "../../utils/decorators/log.js";
4
5
  import __decorate from "../../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
5
6
  import { fromDisposables } from "../../utils/disposable.js";
6
7
  import { Emitter } from "../../utils/event.js";
7
8
  import { gate } from "../../utils/decorators/gate.js";
9
+ import { toError } from "./errors.js";
8
10
  import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, IssuesCloudHostIntegrationId, IssuesSelfManagedHostIntegrationId } from "./constants.js";
9
11
  import { getSupportedCloudIntegrationIds, isSupportedCloudIntegrationId, toIntegrationId } from "./authentication/models.js";
10
12
  import { CloudIntegrationService } from "./authentication/cloudIntegrationService.js";
13
+ import { getRemoteHostMatcher, hostFromDomain, sameConfiguredBaseUrl } from "./utils/domain.utils.js";
11
14
  import { convertRemoteProviderIdToIntegrationId, getIntegrationIdForRemote, isCloudSelfManagedHostIntegrationId, isGitCloudHostIntegrationId, isGitSelfManagedHostIntegrationId, isNonExpiringZeroTokenIntegrationId, isSelfManagedHostIntegrationId, remoteProviderTypeForConfig, remoteProviderTypeForIntegration } from "./utils/integration.utils.js";
12
15
  import { providersMetadata } from "./providers/models.js";
13
- import { hostFromDomain, sameConfiguredBaseUrl } from "./utils/domain.utils.js";
14
16
  import { ConfiguredIntegrationService } from "./authentication/configuredIntegrationService.js";
15
17
  import { IntegrationAuthenticationService } from "./authentication/integrationAuthenticationService.js";
16
18
  import { createApiClients } from "./providers/apiClients.js";
@@ -228,7 +230,7 @@ var IntegrationService = class {
228
230
  if (configuredDomain == null) throw new Error(`Domain is required for '${id}' integration`);
229
231
  integration = new (await (import(
230
232
  /* webpackChunkName: "integrations" */
231
- "./providers/azureDevOps.js"
233
+ "./providers/azureDevOpsServer.js"
232
234
  ))).AzureDevOpsServerIntegration(this.ctx, this.authenticationService, this.getProvidersApi.bind(this), this._onDidChangeIntegrationConnection, configuredDomain);
233
235
  domain = configuredDomain;
234
236
  break;
@@ -237,7 +239,7 @@ var IntegrationService = class {
237
239
  }
238
240
  integration = new (await (import(
239
241
  /* webpackChunkName: "integrations" */
240
- "./providers/azureDevOps.js"
242
+ "./providers/azureDevOpsServer.js"
241
243
  ))).AzureDevOpsServerIntegration(this.ctx, this.authenticationService, this.getProvidersApi.bind(this), this._onDidChangeIntegrationConnection, domain);
242
244
  break;
243
245
  case "jira":
@@ -309,48 +311,51 @@ var IntegrationService = class {
309
311
  return integration?.type === "git" ? integration : void 0;
310
312
  }
311
313
  async getMyIssues(integrationIds, options) {
312
- const integrations = /* @__PURE__ */ new Map();
313
- const hostingIntegrationIds = integrationIds?.filter((id) => id in GitCloudHostIntegrationId || id in GitSelfManagedHostIntegrationId);
314
314
  const openRemotesByIntegrationId = /* @__PURE__ */ new Map();
315
315
  let hasOpenAzureRepository = false;
316
316
  for (const remote of await this.ctx.repositories.getOpenRemotes()) {
317
- const remoteIntegration = await this.getByRemote(remote);
318
- if (remoteIntegration == null) continue;
319
- if (remoteIntegration.id === "azureDevOps") hasOpenAzureRepository = true;
320
- for (const integrationId of hostingIntegrationIds?.length ? hostingIntegrationIds : [...Object.values(GitCloudHostIntegrationId), ...Object.values(GitSelfManagedHostIntegrationId)]) if (remoteIntegration.id === integrationId && remote.provider?.owner != null && remote.provider?.repoName != null) {
321
- const descriptor = {
322
- key: `${remote.provider.owner}/${remote.provider.repoName}`,
323
- owner: remote.provider.owner,
324
- name: remote.provider.repoName
325
- };
326
- if (openRemotesByIntegrationId.has(integrationId)) openRemotesByIntegrationId.get(integrationId)?.push(descriptor);
327
- else openRemotesByIntegrationId.set(integrationId, [descriptor]);
328
- }
317
+ const integrationId = getIntegrationIdForRemote(remote.provider);
318
+ if (integrationId == null) continue;
319
+ if (integrationId === "azureDevOps") hasOpenAzureRepository = true;
320
+ if (remote.provider?.owner == null || remote.provider?.repoName == null) continue;
321
+ const repo = {
322
+ key: `${remote.provider.owner}/${remote.provider.repoName}`,
323
+ owner: remote.provider.owner,
324
+ name: remote.provider.repoName
325
+ };
326
+ const remotes = openRemotesByIntegrationId.get(integrationId);
327
+ if (remotes != null) remotes.push({
328
+ remote,
329
+ repo
330
+ });
331
+ else openRemotesByIntegrationId.set(integrationId, [{
332
+ remote,
333
+ repo
334
+ }]);
329
335
  }
336
+ const integrations = /* @__PURE__ */ new Map();
330
337
  for (const integrationId of integrationIds?.length ? integrationIds : [
331
338
  ...Object.values(GitCloudHostIntegrationId),
332
339
  ...Object.values(IssuesCloudHostIntegrationId),
333
340
  ...Object.values(IssuesSelfManagedHostIntegrationId),
334
341
  ...Object.values(GitSelfManagedHostIntegrationId)
335
342
  ]) {
336
- const integration = await this.get(integrationId);
337
- const isInvalidIntegration = options?.openRepositoriesOnly && integrationId !== "azureDevOps" && (isGitCloudHostIntegrationId(integrationId) || isGitSelfManagedHostIntegrationId(integrationId)) && !openRemotesByIntegrationId.has(integrationId) || integrationId === "azureDevOps" && !hasOpenAzureRepository;
338
- if (integration == null || isInvalidIntegration) continue;
339
- integrations.set(integration, options?.openRepositoriesOnly && !isInvalidIntegration ? openRemotesByIntegrationId.get(integrationId) : void 0);
343
+ if (integrationId === "azureDevOps" && !hasOpenAzureRepository) continue;
344
+ const integrationsForId = await this.getIntegrationsForAccountWideRead(integrationId);
345
+ for (const integration of integrationsForId) {
346
+ const openRemotes = this.getOpenRemotesForHost(integration, integrationsForId, openRemotesByIntegrationId.get(integrationId));
347
+ if (options?.openRepositoriesOnly && integrationId !== "azureDevOps" && (isGitCloudHostIntegrationId(integrationId) || isGitSelfManagedHostIntegrationId(integrationId)) && openRemotes == null) continue;
348
+ integrations.set(integration, options?.openRepositoriesOnly ? openRemotes : void 0);
349
+ }
340
350
  }
341
351
  if (integrations.size === 0) return void 0;
342
352
  return this.getMyIssuesCore(integrations, options?.cancellation);
343
353
  }
344
354
  async getMyIssuesCore(integrations, cancellation) {
345
355
  const start = performance.now();
346
- const promises = [];
347
- for (const [integration, repos] of integrations) {
348
- if (integration == null) continue;
349
- promises.push(integration.searchMyIssuesResult(repos, cancellation));
350
- }
351
- const results = await Promise.allSettled(promises);
356
+ const results = await mapSettledBounded([...integrations], 6, ([integration, repos]) => integration.searchMyIssuesResult(repos, cancellation));
352
357
  const successfulResults = [...flatten(filterMap(results, (r) => r.status === "fulfilled" && r.value?.value != null ? r.value.value : void 0))];
353
- const errors = [...filterMap(results, (r) => r.status === "fulfilled" && r.value?.error != null ? r.value.error : void 0)];
358
+ const errors = [...filterMap(results, (r) => r.status === "rejected" ? toError(r.reason) : r.value?.error)];
354
359
  return {
355
360
  value: successfulResults,
356
361
  error: errors.length === 0 ? void 0 : errors.length === 1 ? errors[0] : new AggregateError(errors, "Failed to get some issues"),
@@ -391,27 +396,15 @@ var IntegrationService = class {
391
396
  }
392
397
  async getMyPullRequests(integrationIds, cancellation, options) {
393
398
  const integrations = /* @__PURE__ */ new Map();
394
- for (const integrationId of integrationIds?.length ? integrationIds : Object.values(GitCloudHostIntegrationId)) {
395
- let integration;
396
- try {
397
- integration = await this.get(integrationId);
398
- } catch {}
399
- if (integration == null) continue;
400
- integrations.set(integration, void 0);
401
- }
399
+ for (const integrationId of integrationIds?.length ? integrationIds : [...Object.values(GitCloudHostIntegrationId), ...Object.values(GitSelfManagedHostIntegrationId)]) for (const integration of await this.getIntegrationsForAccountWideRead(integrationId)) integrations.set(integration, void 0);
402
400
  if (integrations.size === 0) return void 0;
403
401
  return this.getMyPullRequestsCore(integrations, cancellation, options);
404
402
  }
405
403
  async getMyPullRequestsCore(integrations, cancellation, options) {
406
404
  const start = performance.now();
407
- const promises = [];
408
- for (const [integration, repos] of integrations) {
409
- if (integration == null) continue;
410
- promises.push(integration.searchMyPullRequests(repos, cancellation, options));
411
- }
412
- const results = await Promise.allSettled(promises);
405
+ const results = await mapSettledBounded([...integrations], 6, ([integration, repos]) => integration.searchMyPullRequests(repos, cancellation, options));
413
406
  const successfulResults = [...flatten(filterMap(results, (r) => r.status === "fulfilled" && r.value?.value != null ? r.value.value : void 0))];
414
- const errors = [...filterMap(results, (r) => r.status === "fulfilled" && r.value?.error != null ? r.value.error : void 0)];
407
+ const errors = [...filterMap(results, (r) => r.status === "rejected" ? toError(r.reason) : r.value?.error)];
415
408
  return {
416
409
  value: successfulResults,
417
410
  error: errors.length === 0 ? void 0 : errors.length === 1 ? errors[0] : new AggregateError(errors, "Failed to get some pull requests"),
@@ -481,6 +474,60 @@ var IntegrationService = class {
481
474
  return;
482
475
  }
483
476
  }
477
+ /**
478
+ * Resolves every integration instance an account-wide read must cover for `id`. A cloud id has one instance;
479
+ * a self-managed id has one per configured host, so the read fans out over all of them rather than `get(id)`
480
+ * silently picking whichever host was cached (or flagged primary) first, which dropped every other GitHub
481
+ * Enterprise server, GitLab instance, Bitbucket Data Center, Azure DevOps Server or Jira Data Center the user
482
+ * is connected to (#5873).
483
+ *
484
+ * Hosts come from every configured connection, local as well as cloud: that is the set `get(id)` already picked
485
+ * its primary from, and a locally configured host is as much the user's as a cloud-synced one. This is where it
486
+ * departs from {@link getSupportedCloudIntegrations}, which only walks cloud connections because it exists to
487
+ * sync them. The two share the per-host resolution and the fallback to whatever instance is already cached when
488
+ * no host is configured. A lookup that throws counts as "not available", like {@link getIntegrationForRead}.
489
+ */
490
+ async getIntegrationsForAccountWideRead(id) {
491
+ const hosts = /* @__PURE__ */ new Set();
492
+ if (isSelfManagedHostIntegrationId(id)) for (const { domain } of this.getConfigured(id)) {
493
+ const host = hostFromDomain(domain) ?? domain;
494
+ if (host) hosts.add(host);
495
+ }
496
+ const targets = hosts.size !== 0 ? [...hosts] : [void 0];
497
+ const integrations = [];
498
+ for (const host of targets) {
499
+ const integration = await this.get(id, host).catch(() => void 0);
500
+ if (integration != null) integrations.push(integration);
501
+ }
502
+ return integrations;
503
+ }
504
+ async isConnectedForAccountWideRead(id, options) {
505
+ const integrations = await this.getIntegrationsForAccountWideRead(id);
506
+ return (await mapSettledBounded(integrations, 6, async (integration) => {
507
+ return (integration.maybeConnected ?? await integration.isConnected()) && (!options?.access || await integration.access());
508
+ })).some((result) => result.status === "fulfilled" && result.value);
509
+ }
510
+ /**
511
+ * The open repositories to scope `integration`'s read to, out of the remotes open for its integration id.
512
+ *
513
+ * With a single host for the id (every cloud id, and a self-managed id with one configured host) every remote
514
+ * is that host's, as it was before the read fanned out, so a remote addressed through an SSH alias or a custom
515
+ * domain keeps its repositories scoped. With several hosts a remote belongs to the one configured host it
516
+ * names, compared the way {@link resolveRepository} compares them: exactly for a web remote, by hostname for an
517
+ * SSH/git one, whose parsed domain has lost any port. A remote naming no host, or several (two ports on one
518
+ * machine), is credited to none, like `resolveRepository`'s `host-mismatch`: asking one server about another
519
+ * server's `owner/name` reads a different repository, or nothing, under the right name.
520
+ */
521
+ getOpenRemotesForHost(integration, integrationsForId, remotes) {
522
+ if (!remotes?.length) return void 0;
523
+ if (integrationsForId.length <= 1) return remotes.map((r) => r.repo);
524
+ const owned = remotes.filter((r) => {
525
+ const hostsMatch = getRemoteHostMatcher(r.remote.scheme);
526
+ const matching = integrationsForId.filter((i) => hostsMatch(r.remote.provider?.domain, i.domain));
527
+ return matching.length === 1 && matching[0] === integration;
528
+ });
529
+ return owned.length ? owned.map((r) => r.repo) : void 0;
530
+ }
484
531
  resolveDomainForRead(id, connectionId, domain) {
485
532
  if (!isSelfManagedHostIntegrationId(id)) return void 0;
486
533
  return (connectionId != null ? this.getConfiguredConnectionDomain(id, connectionId) : void 0) ?? hostFromDomain(domain) ?? this.primaryConfiguredDomain(id);