@gitkraken/core-gitlens 0.4.100 → 0.4.101

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 (65) hide show
  1. package/CHANGELOG.md +4 -1
  2. package/dist/plus/integrations/errors.d.ts.map +1 -1
  3. package/dist/plus/integrations/errors.js +6 -2
  4. package/dist/plus/integrations/errors.js.map +1 -1
  5. package/dist/plus/integrations/integrationService.d.ts +8 -5
  6. package/dist/plus/integrations/integrationService.d.ts.map +1 -1
  7. package/dist/plus/integrations/integrationService.js +20 -652
  8. package/dist/plus/integrations/integrationService.js.map +1 -1
  9. package/dist/plus/integrations/models/gitHostIntegration.d.ts +10 -17
  10. package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
  11. package/dist/plus/integrations/models/gitHostIntegration.js +38 -88
  12. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  13. package/dist/plus/integrations/models/issuesIntegration.d.ts +4 -21
  14. package/dist/plus/integrations/models/issuesIntegration.d.ts.map +1 -1
  15. package/dist/plus/integrations/models/issuesIntegration.js +5 -47
  16. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  17. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  18. package/dist/plus/integrations/reads/broaden.d.ts +20 -0
  19. package/dist/plus/integrations/reads/broaden.d.ts.map +1 -0
  20. package/dist/plus/integrations/reads/broaden.js +326 -0
  21. package/dist/plus/integrations/reads/broaden.js.map +1 -0
  22. package/dist/plus/integrations/reads/context.d.ts +22 -0
  23. package/dist/plus/integrations/reads/context.d.ts.map +1 -1
  24. package/dist/plus/integrations/reads/hierarchy.d.ts +1 -1
  25. package/dist/plus/integrations/reads/hierarchy.d.ts.map +1 -1
  26. package/dist/plus/integrations/reads/hierarchy.js +109 -137
  27. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  28. package/dist/plus/integrations/reads/hierarchy.utils.d.ts +25 -1
  29. package/dist/plus/integrations/reads/hierarchy.utils.d.ts.map +1 -1
  30. package/dist/plus/integrations/reads/hierarchy.utils.js +43 -0
  31. package/dist/plus/integrations/reads/hierarchy.utils.js.map +1 -1
  32. package/dist/plus/integrations/reads/issueTracker.d.ts +1 -1
  33. package/dist/plus/integrations/reads/issueTracker.d.ts.map +1 -1
  34. package/dist/plus/integrations/reads/issueTracker.js +13 -25
  35. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  36. package/dist/plus/integrations/reads/issues.d.ts +2 -2
  37. package/dist/plus/integrations/reads/issues.d.ts.map +1 -1
  38. package/dist/plus/integrations/reads/issues.js.map +1 -1
  39. package/dist/plus/integrations/reads/pullRequests.d.ts +1 -1
  40. package/dist/plus/integrations/reads/pullRequests.d.ts.map +1 -1
  41. package/dist/plus/integrations/reads/pullRequests.js +1 -1
  42. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  43. package/dist/plus/integrations/reads/resolveRepository.d.ts +25 -0
  44. package/dist/plus/integrations/reads/resolveRepository.d.ts.map +1 -0
  45. package/dist/plus/integrations/reads/resolveRepository.js +235 -0
  46. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -0
  47. package/dist/plus/integrations/reads/sweeps.d.ts +11 -0
  48. package/dist/plus/integrations/reads/sweeps.d.ts.map +1 -0
  49. package/dist/plus/integrations/reads/sweeps.js +125 -0
  50. package/dist/plus/integrations/reads/sweeps.js.map +1 -0
  51. package/package.json +1 -1
  52. package/src/plus/integrations/errors.ts +6 -2
  53. package/src/plus/integrations/integrationService.ts +27 -801
  54. package/src/plus/integrations/models/gitHostIntegration.ts +71 -93
  55. package/src/plus/integrations/models/issuesIntegration.ts +5 -52
  56. package/src/plus/integrations/providers/azureDevOps.ts +1 -1
  57. package/src/plus/integrations/reads/broaden.ts +452 -0
  58. package/src/plus/integrations/reads/context.ts +24 -0
  59. package/src/plus/integrations/reads/hierarchy.ts +133 -153
  60. package/src/plus/integrations/reads/hierarchy.utils.ts +50 -1
  61. package/src/plus/integrations/reads/issueTracker.ts +15 -27
  62. package/src/plus/integrations/reads/issues.ts +4 -2
  63. package/src/plus/integrations/reads/pullRequests.ts +2 -9
  64. package/src/plus/integrations/reads/resolveRepository.ts +272 -0
  65. package/src/plus/integrations/reads/sweeps.ts +185 -0
@@ -104,6 +104,21 @@ export type SearchMyPullRequestsOptions = {
104
104
  includeReviewRequested?: boolean;
105
105
  };
106
106
 
107
+ /**
108
+ * Rejects a read the provider can't serve as asked: logs the reason and returns it as the `{ error }` half of an
109
+ * {@link IntegrationResult}, timed from `start`.
110
+ *
111
+ * A single builder because these refusals are a contract, not incidental returns — the read cores below reject on
112
+ * unsupported input, an inexpressible filter set, or an unresolvable filter account, and each one must reach the
113
+ * caller as an error it can surface as a warning rather than as an empty success. Writing the message once also
114
+ * keeps the logged text and the thrown text from drifting apart, which is exactly what happened while each site
115
+ * spelled its message out twice.
116
+ */
117
+ function unsupportedRead<T>(message: string, start: number, logContext?: string): IntegrationResult<T> {
118
+ Logger.warn(message, logContext);
119
+ return { error: new Error(message), duration: performance.now() - start };
120
+ }
121
+
107
122
  export abstract class GitHostIntegration<
108
123
  ID extends IntegrationIds = IntegrationIds,
109
124
  T extends ResourceDescriptor = ResourceDescriptor,
@@ -289,15 +304,6 @@ export abstract class GitHostIntegration<
289
304
  cancellation?: AbortSignal,
290
305
  ): Promise<RepositoryMetadata | undefined>;
291
306
 
292
- /**
293
- * Lists the organizations (orgs/workspaces/groups) the current user belongs to on this host.
294
- * `truncated === true` means the defensive page-drain backstop stopped before the upstream listing
295
- * was exhausted.
296
- */
297
- async getOrganizationsForUser(): Promise<ProviderHierarchyResult<ProviderOrganization> | undefined> {
298
- return (await this.getOrganizationsForUserResult())?.value;
299
- }
300
-
301
307
  /**
302
308
  * Whether this git host implements generic org discovery. False for providers that register no
303
309
  * {@link getProviderOrganizationsForUser} hook (e.g. Bitbucket Data Center) — the facade uses this to
@@ -344,9 +350,13 @@ export abstract class GitHostIntegration<
344
350
  }
345
351
 
346
352
  /**
347
- * Result-returning core of {@link getOrganizationsForUser}. Resolves the session for `connectionId`
348
- * (or the primary connection when omitted, honoring multi-account reads) and recovers a thrown error
349
- * into `{ error }` so callers can surface it as a warning instead of swallowing it to `undefined`.
353
+ * Lists the organizations (orgs/workspaces/groups) the current user belongs to on this host.
354
+ * `truncated === true` means the defensive page-drain backstop stopped before the upstream listing was
355
+ * exhausted.
356
+ *
357
+ * Resolves the session for `connectionId` (or the primary connection when omitted, honoring multi-account
358
+ * reads) and recovers a thrown error into `{ error }` so callers can surface it as a warning instead of
359
+ * swallowing it to `undefined`.
350
360
  */
351
361
  @trace()
352
362
  async getOrganizationsForUserResult(
@@ -416,17 +426,9 @@ export abstract class GitHostIntegration<
416
426
  * every other host ignores it. (Azure without a project can't page its cross-project merge, so it
417
427
  * returns all matches in a single page.) `truncated === true` means the defensive page-drain
418
428
  * backstop stopped before the upstream listing was exhausted.
419
- */
420
- async getRepositoriesForOrg(
421
- org: string,
422
- options?: { project?: string; cursor?: string },
423
- ): Promise<ProviderHierarchyResult<ProviderRepository> | undefined> {
424
- return (await this.getRepositoriesForOrgResult(org, options))?.value;
425
- }
426
-
427
- /**
428
- * Result-returning core of {@link getRepositoriesForOrg}. Resolves the session for `connectionId`
429
- * (or the primary connection when omitted) and recovers a thrown error into `{ error }` for warnings.
429
+ *
430
+ * Resolves the session for `connectionId` (or the primary connection when omitted) and recovers a thrown
431
+ * error into `{ error }`, so a caller surfaces it as a warning instead of a silent `undefined`.
430
432
  */
431
433
  @trace()
432
434
  async getRepositoriesForOrgResult(
@@ -706,11 +708,7 @@ export abstract class GitHostIntegration<
706
708
  (isAzureDevOpsProvider(providerId) &&
707
709
  !reposOrRepoIds.every(repo => repo.project != null && repo.namespace != null)))
708
710
  ) {
709
- Logger.warn(`Unsupported input for provider ${providerId}`, 'getIssuesForRepos');
710
- return {
711
- error: new Error(`Unsupported input for provider ${providerId}`),
712
- duration: performance.now() - start,
713
- };
711
+ return unsupportedRead(`Unsupported input for provider ${providerId}`, start, 'getIssuesForRepos');
714
712
  }
715
713
 
716
714
  let getIssuesOptions: GetIssuesOptions | undefined;
@@ -723,28 +721,24 @@ export abstract class GitHostIntegration<
723
721
  }
724
722
 
725
723
  if (organizations.size > 1) {
726
- Logger.warn(`Multiple organizations not supported for provider ${providerId}`, 'getIssuesForRepos');
727
- return {
728
- error: new Error(`Multiple organizations not supported for provider ${providerId}`),
729
- duration: performance.now() - start,
730
- };
724
+ return unsupportedRead(
725
+ `Multiple organizations not supported for provider ${providerId}`,
726
+ start,
727
+ 'getIssuesForRepos',
728
+ );
731
729
  } else if (organizations.size === 0) {
732
- Logger.warn(`No organizations found for provider ${providerId}`, 'getIssuesForRepos');
733
- return {
734
- error: new Error(`No organizations found for provider ${providerId}`),
735
- duration: performance.now() - start,
736
- };
730
+ return unsupportedRead(`No organizations found for provider ${providerId}`, start, 'getIssuesForRepos');
737
731
  }
738
732
 
739
733
  const organization: string = first(organizations.values())!;
740
734
 
741
735
  if (options?.filters != null) {
742
736
  if (!api.providerSupportsIssueFilters(providerId, options.filters)) {
743
- Logger.warn(`Unsupported filters for provider ${providerId}`, 'getIssuesForRepos');
744
- return {
745
- error: new Error(`Unsupported filters for provider ${providerId}`),
746
- duration: performance.now() - start,
747
- };
737
+ return unsupportedRead(
738
+ `Unsupported filters for provider ${providerId}`,
739
+ start,
740
+ 'getIssuesForRepos',
741
+ );
748
742
  }
749
743
 
750
744
  let userAccount: ProviderAccount | undefined;
@@ -756,21 +750,17 @@ export abstract class GitHostIntegration<
756
750
  }
757
751
 
758
752
  if (userAccount == null) {
759
- Logger.warn(`Unable to get current user for ${providerId}`, 'getIssuesForRepos');
760
- return {
761
- error: new Error(`Unable to get current user for ${providerId}`),
762
- duration: performance.now() - start,
763
- };
753
+ return unsupportedRead(`Unable to get current user for ${providerId}`, start, 'getIssuesForRepos');
764
754
  }
765
755
 
766
756
  const userFilterProperty = userAccount.name;
767
757
 
768
758
  if (userFilterProperty == null) {
769
- Logger.warn(`Unable to get user property for filter for ${providerId}`, 'getIssuesForRepos');
770
- return {
771
- error: new Error(`Unable to get user property for filter for ${providerId}`),
772
- duration: performance.now() - start,
773
- };
759
+ return unsupportedRead(
760
+ `Unable to get user property for filter for ${providerId}`,
761
+ start,
762
+ 'getIssuesForRepos',
763
+ );
774
764
  }
775
765
 
776
766
  getIssuesOptions = {
@@ -889,11 +879,7 @@ export abstract class GitHostIntegration<
889
879
  // branch above applies. Without it an unsupported filter (e.g. GitLab has no Mention endpoint) would
890
880
  // resolve to no filter property being set and silently degrade to an unfiltered, project-wide read.
891
881
  if (!api.providerSupportsIssueFilters(providerId, options.filters)) {
892
- Logger.warn(`Unsupported filters for provider ${providerId}`, 'getIssuesForRepos');
893
- return {
894
- error: new Error(`Unsupported filters for provider ${providerId}`),
895
- duration: performance.now() - start,
896
- };
882
+ return unsupportedRead(`Unsupported filters for provider ${providerId}`, start, 'getIssuesForRepos');
897
883
  }
898
884
 
899
885
  let userAccount: ProviderAccount | undefined;
@@ -905,20 +891,16 @@ export abstract class GitHostIntegration<
905
891
  }
906
892
 
907
893
  if (userAccount == null) {
908
- Logger.warn(`Unable to get current user for ${providerId}`, 'getIssuesForRepos');
909
- return {
910
- error: new Error(`Unable to get current user for ${providerId}`),
911
- duration: performance.now() - start,
912
- };
894
+ return unsupportedRead(`Unable to get current user for ${providerId}`, start, 'getIssuesForRepos');
913
895
  }
914
896
 
915
897
  const userFilterProperty = userAccount.username;
916
898
  if (userFilterProperty == null) {
917
- Logger.warn(`Unable to get user property for filter for ${providerId}`, 'getIssuesForRepos');
918
- return {
919
- error: new Error(`Unable to get user property for filter for ${providerId}`),
920
- duration: performance.now() - start,
921
- };
899
+ return unsupportedRead(
900
+ `Unable to get user property for filter for ${providerId}`,
901
+ start,
902
+ 'getIssuesForRepos',
903
+ );
922
904
  }
923
905
 
924
906
  getIssuesOptions = {
@@ -1124,21 +1106,17 @@ export abstract class GitHostIntegration<
1124
1106
  (isAzureDevOpsProvider(providerId) &&
1125
1107
  !reposOrRepoIds.every(repo => repo.project != null && repo.namespace != null)))
1126
1108
  ) {
1127
- Logger.warn(`Unsupported input for provider ${providerId}`);
1128
- return {
1129
- error: new Error(`Unsupported input for provider ${providerId}`),
1130
- duration: performance.now() - start,
1131
- };
1109
+ return unsupportedRead(`Unsupported input for provider ${providerId}`, start);
1132
1110
  }
1133
1111
 
1134
1112
  let getPullRequestsOptions: GetPullRequestsOptions | undefined;
1135
1113
  if (options?.filters != null) {
1136
1114
  if (!api.providerSupportsPullRequestFilters(providerId, options.filters)) {
1137
- Logger.warn(`Unsupported filters for provider ${providerId}`, 'getPullRequestsForRepos');
1138
- return {
1139
- error: new Error(`Unsupported filters for provider ${providerId}`),
1140
- duration: performance.now() - start,
1141
- };
1115
+ return unsupportedRead(
1116
+ `Unsupported filters for provider ${providerId}`,
1117
+ start,
1118
+ 'getPullRequestsForRepos',
1119
+ );
1142
1120
  }
1143
1121
 
1144
1122
  let userAccount: ProviderAccount | undefined;
@@ -1158,11 +1136,11 @@ export abstract class GitHostIntegration<
1158
1136
  duration: performance.now() - start,
1159
1137
  };
1160
1138
  } else if (organizations.size === 0) {
1161
- Logger.warn(`No organizations found for provider ${providerId}`, 'getPullRequestsForRepos');
1162
- return {
1163
- error: new Error(`No organizations found for provider ${providerId}`),
1164
- duration: performance.now() - start,
1165
- };
1139
+ return unsupportedRead(
1140
+ `No organizations found for provider ${providerId}`,
1141
+ start,
1142
+ 'getPullRequestsForRepos',
1143
+ );
1166
1144
  }
1167
1145
 
1168
1146
  const organization: string = first(organizations.values())!;
@@ -1182,11 +1160,11 @@ export abstract class GitHostIntegration<
1182
1160
  }
1183
1161
 
1184
1162
  if (userAccount == null) {
1185
- Logger.warn(`Unable to get current user for ${providerId}`, 'getPullRequestsForRepos');
1186
- return {
1187
- error: new Error(`Unable to get current user for ${providerId}`),
1188
- duration: performance.now() - start,
1189
- };
1163
+ return unsupportedRead(
1164
+ `Unable to get current user for ${providerId}`,
1165
+ start,
1166
+ 'getPullRequestsForRepos',
1167
+ );
1190
1168
  }
1191
1169
 
1192
1170
  let userFilterProperty: string | null;
@@ -1202,11 +1180,11 @@ export abstract class GitHostIntegration<
1202
1180
  }
1203
1181
 
1204
1182
  if (userFilterProperty == null) {
1205
- Logger.warn(`Unable to get user property for filter for ${providerId}`, 'getPullRequestsForRepos');
1206
- return {
1207
- error: new Error(`Unable to get user property for filter for ${providerId}`),
1208
- duration: performance.now() - start,
1209
- };
1183
+ return unsupportedRead(
1184
+ `Unable to get user property for filter for ${providerId}`,
1185
+ start,
1186
+ 'getPullRequestsForRepos',
1187
+ );
1210
1188
  }
1211
1189
 
1212
1190
  // Route the "review requested from me" filter to the field each provider actually reads:
@@ -87,31 +87,13 @@ export abstract class IssuesIntegration<
87
87
 
88
88
  protected abstract getProviderResourcesForUser(session: ProviderAuthenticationSession): Promise<T[] | undefined>;
89
89
 
90
- @trace()
91
- async getProjectsForResources(resources: T[], connectionId?: string): Promise<T[] | undefined> {
92
- return (await this.getProjectsForResourcesResult(resources, connectionId))?.value;
93
- }
94
-
95
- /**
96
- * Result-returning core of {@link getProjectsForResources}. Recovers thrown errors into `{ error }` so callers
97
- * can surface them as warnings rather than silently swallowing them to `undefined`. Implemented by unwrapping
98
- * the metadata-aware path's `values` so the array-returning contract stays backward compatible.
99
- */
100
- async getProjectsForResourcesResult(
101
- resources: T[],
102
- connectionId?: string,
103
- ): Promise<IntegrationResult<T[] | undefined>> {
104
- const result = await this.getProjectsForResourcesWithMetadataResult(resources, connectionId);
105
- if (result == null) return undefined;
106
- if (result.error != null) return { value: result.value?.values, error: result.error };
107
- return { value: result.value?.values };
108
- }
109
-
110
90
  /**
111
- * Metadata-aware counterpart of {@link getProjectsForResourcesResult} for ProviderBackend composition:
112
- * returns the SDK collection `{ values, metadata }` (completeness + per-resource failures) so callers can
113
- * warn on failed resources and set `fetchFailed` without discarding the resources that succeeded.
91
+ * Project discovery for a set of resources, returning the SDK collection `{ values, metadata }` (completeness
92
+ * + per-resource failures) so callers can warn on failed resources and set `fetchFailed` without discarding
93
+ * the resources that succeeded. Thrown errors are recovered into `{ error }` for the same reason: a caller
94
+ * surfaces them as a warning rather than swallowing them to `undefined`.
114
95
  */
96
+ @trace()
115
97
  async getProjectsForResourcesWithMetadataResult(
116
98
  resources: T[],
117
99
  connectionId?: string,
@@ -131,35 +113,6 @@ export abstract class IssuesIntegration<
131
113
  }
132
114
  }
133
115
 
134
- async getProjectsForUser(connectionId?: string): Promise<T[] | undefined> {
135
- return (await this.getProjectsForUserResult(connectionId))?.value;
136
- }
137
-
138
- /**
139
- * Result-returning core of {@link getProjectsForUser}. Composes the resource and project result methods so
140
- * callers can surface per-step warnings. Unwraps the metadata-aware path for the array-returning contract.
141
- */
142
- async getProjectsForUserResult(connectionId?: string): Promise<IntegrationResult<T[] | undefined>> {
143
- const result = await this.getProjectsForUserWithMetadataResult(connectionId);
144
- if (result == null) return undefined;
145
- if (result.error != null) return { value: result.value?.values, error: result.error };
146
- return { value: result.value?.values };
147
- }
148
-
149
- /**
150
- * Metadata-aware counterpart of {@link getProjectsForUserResult}: composes resource discovery with the
151
- * metadata-aware project read so ProviderBackend consumers get completeness/failures across resources.
152
- */
153
- async getProjectsForUserWithMetadataResult(
154
- connectionId?: string,
155
- ): Promise<IntegrationResult<ProviderApiCollectionResult<T> | undefined>> {
156
- const resources = await this.getResourcesForUserResult(connectionId);
157
- if (resources?.error != null) return { error: resources.error };
158
- if (resources?.value == null) return undefined;
159
-
160
- return this.getProjectsForResourcesWithMetadataResult(resources.value, connectionId);
161
- }
162
-
163
116
  /**
164
117
  * Metadata-aware provider project discovery. The default wraps the array-returning
165
118
  * {@link getProviderProjectsForResources} in `{ values }` with no metadata, so providers without a
@@ -1062,7 +1062,7 @@ export abstract class AzureDevOpsIntegrationBase<
1062
1062
  { providerId: this.id, resourceId: p.resourceId, projectId: p.name },
1063
1063
  );
1064
1064
  return {
1065
- issues: result.values.map(i => fromProviderIssue(i, this as any, { project: p })),
1065
+ issues: result.values.map(i => fromProviderIssue(i, this, { project: p })),
1066
1066
  // Azure work-item ids are organization-scoped. Include both org and project so the
1067
1067
  // assigned/authored passes dedupe the same item without collapsing another org's item.
1068
1068
  projectKey: `${p.resourceId}:${p.id}`,