@gitkraken/core-gitlens 0.21.0 → 0.22.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 (61) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/dist/git/utils/autolink.utils.d.ts +4 -1
  3. package/dist/git/utils/autolink.utils.js +12 -3
  4. package/dist/git/utils/autolink.utils.js.map +1 -1
  5. package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts +7 -0
  6. package/dist/plus/integrations/authentication/configuredIntegrationService.js +12 -0
  7. package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
  8. package/dist/plus/integrations/authentication/integrationAuthenticationService.js +5 -3
  9. package/dist/plus/integrations/authentication/integrationAuthenticationService.js.map +1 -1
  10. package/dist/plus/integrations/authentication/models.d.ts +9 -0
  11. package/dist/plus/integrations/authentication/models.js +13 -1
  12. package/dist/plus/integrations/authentication/models.js.map +1 -1
  13. package/dist/plus/integrations/authentication/observedAuthenticationProvider.d.ts +34 -0
  14. package/dist/plus/integrations/authentication/observedAuthenticationProvider.js +227 -0
  15. package/dist/plus/integrations/authentication/observedAuthenticationProvider.js.map +1 -0
  16. package/dist/plus/integrations/index.d.ts +4 -2
  17. package/dist/plus/integrations/index.js.map +1 -1
  18. package/dist/plus/integrations/integrationService.d.ts +9 -4
  19. package/dist/plus/integrations/integrationService.js +30 -7
  20. package/dist/plus/integrations/integrationService.js.map +1 -1
  21. package/dist/plus/integrations/manager.d.ts +18 -1
  22. package/dist/plus/integrations/models/integration.d.ts +8 -1
  23. package/dist/plus/integrations/models/integration.js +79 -10
  24. package/dist/plus/integrations/models/integration.js.map +1 -1
  25. package/dist/plus/integrations/models/issuesIntegration.d.ts +20 -1
  26. package/dist/plus/integrations/models/issuesIntegration.js +106 -2
  27. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  28. package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
  29. package/dist/plus/integrations/providers/jira-server.d.ts +10 -2
  30. package/dist/plus/integrations/providers/jira-server.js +74 -7
  31. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  32. package/dist/plus/integrations/providers/jira.d.ts +3 -3
  33. package/dist/plus/integrations/providers/jira.js +5 -27
  34. package/dist/plus/integrations/providers/jira.js.map +1 -1
  35. package/dist/plus/integrations/providers/linear.d.ts +1 -1
  36. package/dist/plus/integrations/providers/linear.js +3 -27
  37. package/dist/plus/integrations/providers/linear.js.map +1 -1
  38. package/dist/plus/integrations/providers/providerApiClient.js +4 -1
  39. package/dist/plus/integrations/providers/providerApiClient.js.map +1 -1
  40. package/dist/plus/integrations/providers/providerErrors.d.ts +4 -1
  41. package/dist/plus/integrations/providers/providerErrors.js +13 -4
  42. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  43. package/dist/plus/integrations/providers/providersApi.js +1 -1
  44. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  45. package/dist/plus/integrations/providers/trello.d.ts +1 -1
  46. package/dist/plus/integrations/providers/trello.js +1 -1
  47. package/dist/plus/integrations/providers/trello.js.map +1 -1
  48. package/dist/plus/integrations/providers/utils/issueAutolinks.d.ts +8 -0
  49. package/dist/plus/integrations/providers/utils/issueAutolinks.js +20 -0
  50. package/dist/plus/integrations/providers/utils/issueAutolinks.js.map +1 -0
  51. package/dist/plus/integrations/reads/autolinks.d.ts +7 -0
  52. package/dist/plus/integrations/reads/autolinks.js +40 -0
  53. package/dist/plus/integrations/reads/autolinks.js.map +1 -0
  54. package/dist/plus/integrations/reads/context.d.ts +4 -2
  55. package/dist/plus/integrations/reads/drains.d.ts +0 -1
  56. package/dist/plus/integrations/reads/hierarchy.js +1 -1
  57. package/dist/plus/integrations/reads/issueBatch.js +1 -1
  58. package/dist/plus/integrations/reads/issueTracker.js +1 -1
  59. package/dist/plus/integrations/reads/resolveRepository.js +1 -1
  60. package/docs/integrations.md +22 -1
  61. package/package.json +1 -1
@@ -1,8 +1,9 @@
1
1
  import { Event } from "../../utils/event.js";
2
+ import { RefSet } from "../../git/models/autolink.js";
2
3
  import { IssueSearchCriteria, IssueShape, IssueSorting, IssueStateFilter } from "../../git/models/issue.js";
3
4
  import { PullRequestFilter, PullRequestSearchCriteria, PullRequestShape, PullRequestStateFilter } from "../../git/models/pullRequest.js";
4
5
  import { ConfiguredIntegrationDescriptor } from "./authentication/models.js";
5
- import { IntegrationIds } from "./constants.js";
6
+ import { IntegrationIds, IssuesCloudHostIntegrationId, IssuesSelfManagedHostIntegrationId } from "./constants.js";
6
7
  import { ConfiguredIntegrationsChangeEvent } from "./authentication/configuredIntegrationService.js";
7
8
  import { ConnectionStateChangeEvent, ProviderBroadenResult, ProviderOrganization, ProviderPagedResult, ProviderRepositoryShape, ProviderResult, ProviderSweepResult, ResolveRepositoryResult } from "./results.js";
8
9
  import { IssueFilter } from "./providerFilters.js";
@@ -214,6 +215,14 @@ export type ListProjectsOptions = {
214
215
  connectionId?: never;
215
216
  domain?: never;
216
217
  };
218
+ export type GetAutolinkReferencesOptions = {
219
+ providerId: IssuesCloudHostIntegrationId.Jira | IssuesSelfManagedHostIntegrationId.JiraServer;
220
+ connectionId?: string;
221
+ /** Trusted self-managed domain fallback, as on the other provider reads. */
222
+ domain?: string;
223
+ /** Drops discovery and awaits a fresh build; retires builds started before the refresh. */
224
+ forceSync?: boolean;
225
+ };
217
226
  /**
218
227
  * Public, provider-neutral integration facade. Provider clients and integration model instances remain private
219
228
  * implementation details so SDK changes don't expand this contract.
@@ -281,6 +290,14 @@ export interface IntegrationManager {
281
290
  deleteConnection(id: IntegrationIds, connectionId: string): Promise<void>;
282
291
  listOrgs(options?: ListOrgsOptions): Promise<ProviderResult<ProviderOrganization>>;
283
292
  listProjects(options?: ListProjectsOptions): Promise<ProviderResult<ProviderOrganization>>;
293
+ /**
294
+ * Jira project references for one connection. Cold discovery is awaited. Returns `undefined` for an
295
+ * unresolved target or failed/incomplete discovery; an empty set means complete discovery found no links.
296
+ * Each call reads the provider; callers own coalescing and caching.
297
+ * Match with `getAutolinks` or `getBranchAutolinks` from `git/utils/autolink.utils.js` in the host process;
298
+ * references can contain compiled regular expressions and are not a wire format.
299
+ */
300
+ getAutolinkReferences(options: GetAutolinkReferencesOptions): Promise<RefSet | undefined>;
284
301
  listRepos(options: {
285
302
  providerId: IntegrationIds;
286
303
  org?: string;
@@ -7,7 +7,7 @@ import { Issue as Issue$1, IssueShape, IssueSyncedWith } from "../../../git/mode
7
7
  import { PullRequest, PullRequestMergeableState, PullRequestReviewDecision, PullRequestReviewState, PullRequestStatusCheckRollupState } from "../../../git/models/pullRequest.js";
8
8
  import { ScopedLogger } from "../../../utils/logger.scoped.js";
9
9
  import { Account as Account$1 } from "../../../git/models/author.js";
10
- import { ConnectionResource, ProviderAuthenticationSession } from "../authentication/models.js";
10
+ import { ConnectionResource, ProviderAuthenticationSession, SessionPublication } from "../authentication/models.js";
11
11
  import { GitCloudHostIntegrationId, IntegrationIds, IssuesCloudHostIntegrationId, IssuesHostIntegrationIds } from "../constants.js";
12
12
  import { Sources } from "../telemetry.js";
13
13
  import { ProviderWarningCause, ProviderWarningScope } from "../results.js";
@@ -300,8 +300,15 @@ export declare abstract class IntegrationBase<ID extends IntegrationIds = Integr
300
300
  * otherwise stay invisible, or keep being searched, for the rest of the session (#5907).
301
301
  */
302
302
  invalidateDiscoveryCaches(): void;
303
+ invalidateDiscoveryForConnection(_connectionId?: string): void;
304
+ getSessionDiscoveryGeneration(_sessionId?: string, _connectionId?: string, _cloud?: boolean): number;
305
+ get discoveryGeneration(): number;
306
+ protected isResolvedSessionCurrent(_session: ProviderAuthenticationSession | undefined, _generation: number): boolean;
307
+ onSessionPublishing(publication: SessionPublication): void;
308
+ protected invalidatePublishedSession(_publication: SessionPublication, _affectsPrimary: boolean): void;
303
309
  private static readonly requestExceptionLimit;
304
310
  private requestExceptionCount;
311
+ private resetRecoveryAccounting;
305
312
  resetRequestExceptionCount(syncReqUsecase: SyncReqUsecase | 'all'): void;
306
313
  /**
307
314
  * Resets request exceptions without resetting the amount of syncs
@@ -1,4 +1,4 @@
1
- import { isCancellationError } from "../../../utils/cancellation.js";
1
+ import { CancellationError, isCancellationError } from "../../../utils/cancellation.js";
2
2
  import { Logger as _logger } from "../../../utils/logger.js";
3
3
  import { getScopedLogger } from "../../../utils/logger.scoped.js";
4
4
  import { debug, trace } from "../../../utils/decorators/log.js";
@@ -12,6 +12,7 @@ import { isProviderUnreachableError, toError } from "../errors.js";
12
12
  import "../constants.js";
13
13
  import { isAzureProviderId } from "../providers/providerErrors.js";
14
14
  import { attributeScopedAuthFailures, hasOnlyScopedAuthFailures, markCredentialRefusals, toCollectionScopeFailure } from "../collectionMetadata.js";
15
+ import { getSessionIdentity } from "../authentication/models.js";
15
16
  import { areDomainsOnSameHost } from "../utils/domain.utils.js";
16
17
  import { isSelfManagedHostIntegrationId } from "../utils/integration.utils.js";
17
18
  import { throwIfAllSettledFailed } from "../providers/utils/providerPaging.js";
@@ -118,6 +119,7 @@ var IntegrationBase = class IntegrationBase {
118
119
  this.didChangeConnection = didChangeConnection;
119
120
  }
120
121
  dispose() {
122
+ this.invalidateDiscoveryCaches();
121
123
  this._onDidChange.dispose();
122
124
  }
123
125
  get authProviderDescriptor() {
@@ -365,13 +367,41 @@ var IntegrationBase = class IntegrationBase {
365
367
  * otherwise stay invisible, or keep being searched, for the rest of the session (#5907).
366
368
  */
367
369
  invalidateDiscoveryCaches() {}
370
+ invalidateDiscoveryForConnection(_connectionId) {
371
+ this.invalidateDiscoveryCaches();
372
+ }
373
+ getSessionDiscoveryGeneration(_sessionId, _connectionId, _cloud) {
374
+ return this.discoveryGeneration;
375
+ }
376
+ get discoveryGeneration() {
377
+ return 0;
378
+ }
379
+ isResolvedSessionCurrent(_session, _generation) {
380
+ return true;
381
+ }
382
+ onSessionPublishing(publication) {
383
+ const { session, previousSessionId, replacePrimary } = publication;
384
+ const cloud = session != null ? session.cloud : publication.cloud;
385
+ const sameVariant = cloud == null || this._session?.cloud == null || cloud === this._session.cloud;
386
+ const affectsPrimary = replacePrimary === true && (session != null || cloud == null || this._session != null && sameVariant) || this._session === null && session != null || this._session != null && sameVariant && (session?.id === this._session.id || previousSessionId === this._session.id);
387
+ if (affectsPrimary && getSessionIdentity(this._session) !== getSessionIdentity(session)) this._session = void 0;
388
+ this.invalidatePublishedSession(publication, affectsPrimary);
389
+ }
390
+ invalidatePublishedSession(_publication, _affectsPrimary) {
391
+ this.invalidateDiscoveryCaches();
392
+ }
368
393
  static requestExceptionLimit = 5;
369
394
  requestExceptionCount = 0;
395
+ resetRecoveryAccounting() {
396
+ this.requestExceptionCount = 0;
397
+ this._syncRequestsPerFailedUsecase.clear();
398
+ this._rejectedTokens.clear();
399
+ }
370
400
  resetRequestExceptionCount(syncReqUsecase) {
371
401
  this.requestExceptionCount = 0;
372
402
  if (syncReqUsecase === "all") {
373
- this._syncRequestsPerFailedUsecase.clear();
374
- this.onStoredTokensReplaced();
403
+ this.resetRecoveryAccounting();
404
+ this.invalidateDiscoveryCaches();
375
405
  } else this._syncRequestsPerFailedUsecase.delete(syncReqUsecase);
376
406
  }
377
407
  /**
@@ -416,7 +446,7 @@ var IntegrationBase = class IntegrationBase {
416
446
  syncCloudConnection(state, forceSync) {
417
447
  return this.runSessionTransition(() => this.syncCloudConnectionLocked(state, forceSync));
418
448
  }
419
- async syncCloudConnectionLocked(state, forceSync) {
449
+ async syncCloudConnectionLocked(state, forceSync, reason = "explicit") {
420
450
  const scope = getScopedLogger();
421
451
  if (this._session?.cloud === false && state !== "connected") {
422
452
  if (this.id !== "github" && !this.skippedNonCloudReported) {
@@ -435,7 +465,10 @@ var IntegrationBase = class IntegrationBase {
435
465
  const oldSession = this._session;
436
466
  let resyncing = false;
437
467
  if (forceSync) {
438
- this.onStoredTokensReplaced();
468
+ if (reason === "explicit") {
469
+ this._rejectedTokens.clear();
470
+ this.invalidateDiscoveryForConnection();
471
+ }
439
472
  await this.ctx.storage.deleteWorkspace(this.connectedKey);
440
473
  resyncing = true;
441
474
  } else {
@@ -454,7 +487,7 @@ var IntegrationBase = class IntegrationBase {
454
487
  if (!isCancellationError(ex)) scope?.error(ex);
455
488
  failure = toError(ex);
456
489
  }
457
- if (oldSession && newSession && newSession.accessToken !== oldSession.accessToken) this.resetRequestExceptionCount("all");
490
+ if (oldSession && newSession && newSession === this._session && newSession.accessToken !== oldSession.accessToken) this.resetRecoveryAccounting();
458
491
  if (resyncing && newSession == null && !refetchFailed) await (await this.authenticationService.get(this.authProvider.id)).deleteSession(this.authProviderDescriptor, { preserveConfigured: false });
459
492
  break;
460
493
  }
@@ -660,7 +693,7 @@ var IntegrationBase = class IntegrationBase {
660
693
  let failure;
661
694
  if (this._resyncs !== resyncs) failure = this._lastResyncFailure;
662
695
  else if (this._session != null && this.connectionExpired === true) try {
663
- failure = await this.syncCloudConnectionLocked("connected", true);
696
+ failure = await this.syncCloudConnectionLocked("connected", true, "expired");
664
697
  } catch (ex) {
665
698
  failure = toError(ex);
666
699
  }
@@ -719,8 +752,14 @@ var IntegrationBase = class IntegrationBase {
719
752
  this._session = null;
720
753
  }
721
754
  if (this.ctx.config.isIntegrationsEnabled?.() === false) return void 0;
722
- if (createIfNeeded || sync) await this.ctx.storage.deleteWorkspace(this.connectedKey);
723
- else if (this.ctx.storage.getWorkspace(this.connectedKey) === false) return;
755
+ const generation = this.discoveryGeneration;
756
+ if (createIfNeeded || sync) {
757
+ await this.ctx.storage.deleteWorkspace(this.connectedKey);
758
+ if (this.getSessionDiscoveryGeneration(this._session?.id, void 0, this._session?.cloud) > generation) {
759
+ if (sync) throw new CancellationError();
760
+ return;
761
+ }
762
+ } else if (this.ctx.storage.getWorkspace(this.connectedKey) === false) return;
724
763
  let session;
725
764
  try {
726
765
  session = await (await this.authenticationService.get(this.authProvider.id)).getSession(this.authProviderDescriptor, sync ? {
@@ -734,7 +773,15 @@ var IntegrationBase = class IntegrationBase {
734
773
  if (session?.expiresAt != null && session.expiresAt < /* @__PURE__ */ new Date()) session = null;
735
774
  if (session != null && !this.isSessionForIntegrationHost(session)) session = void 0;
736
775
  } catch (ex) {
776
+ if (isCancellationError(ex) || !this.isResolvedSessionCurrent(void 0, generation)) {
777
+ if (sync) throw new CancellationError();
778
+ return;
779
+ }
737
780
  await this.ctx.storage.deleteWorkspace(this.connectedKey);
781
+ if (!this.isResolvedSessionCurrent(void 0, generation)) {
782
+ if (sync) throw new CancellationError();
783
+ return;
784
+ }
738
785
  if (!sync && ex instanceof Error && ex.message.includes("User did not consent")) return;
739
786
  if (sync) {
740
787
  this.smoothifyRequestExceptionCount();
@@ -742,18 +789,40 @@ var IntegrationBase = class IntegrationBase {
742
789
  }
743
790
  session = null;
744
791
  }
745
- if (session === void 0 && !createIfNeeded && !sync) await this.ctx.storage.deleteWorkspace(this.connectedKey);
792
+ if (!this.isResolvedSessionCurrent(session ?? void 0, generation)) {
793
+ if (sync) throw new CancellationError();
794
+ return;
795
+ }
796
+ if (session === void 0 && !createIfNeeded && !sync) {
797
+ await this.ctx.storage.deleteWorkspace(this.connectedKey);
798
+ if (!this.isResolvedSessionCurrent(void 0, generation)) return void 0;
799
+ }
800
+ if (getSessionIdentity(this._session) !== getSessionIdentity(session)) this.onSessionPublishing({
801
+ session: session ?? void 0,
802
+ replacePrimary: true
803
+ });
804
+ else if (refetch && session != null) this.onSessionPublishing({
805
+ session,
806
+ replacePrimary: true
807
+ });
746
808
  this._session = session ?? null;
747
809
  this.smoothifyRequestExceptionCount();
748
810
  if (session != null) {
811
+ const publishedGeneration = this.getSessionDiscoveryGeneration(session.id, void 0, session.cloud);
749
812
  await this.ctx.storage.storeWorkspace(this.connectedKey, true);
813
+ if (this._session !== session || publishedGeneration !== this.getSessionDiscoveryGeneration(session.id, void 0, session.cloud)) {
814
+ if (sync) throw new CancellationError();
815
+ return;
816
+ }
750
817
  queueMicrotask(() => {
818
+ if (this._session !== session || publishedGeneration !== this.getSessionDiscoveryGeneration(session.id, void 0, session.cloud)) return;
751
819
  this._onDidChange.fire();
752
820
  this.didChangeConnection?.fire({
753
821
  integration: this,
754
822
  key: this.key,
755
823
  reason: "connected"
756
824
  });
825
+ if (this._session !== session || publishedGeneration !== this.getSessionDiscoveryGeneration(session.id, void 0, session.cloud)) return;
757
826
  (async () => {
758
827
  try {
759
828
  await this.providerOnConnect?.();