@gitkraken/core-gitlens 0.11.0 → 0.12.0

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 (71) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/dist/git/cache.js +2 -0
  3. package/dist/git/cache.js.map +1 -1
  4. package/dist/git/models/issue.d.ts +3 -1
  5. package/dist/git/models/issue.js.map +1 -1
  6. package/dist/git/providers/branches.d.ts +4 -2
  7. package/dist/git/utils/issue.utils.d.ts +3 -1
  8. package/dist/git/utils/issue.utils.js +5 -1
  9. package/dist/git/utils/issue.utils.js.map +1 -1
  10. package/dist/git/watching/worktreeSummary.d.ts +20 -9
  11. package/dist/git/watching/worktreeSummary.js +24 -22
  12. package/dist/git/watching/worktreeSummary.js.map +1 -1
  13. package/dist/git-cli/providers/branches.js +26 -4
  14. package/dist/git-cli/providers/branches.js.map +1 -1
  15. package/dist/plus/integrations/models/gitHostIntegration.js +3 -1
  16. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  17. package/dist/plus/integrations/models/integration.js +1 -1
  18. package/dist/plus/integrations/providers/azure/azure.js +3 -2
  19. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  20. package/dist/plus/integrations/providers/azure/models.d.ts +27 -10
  21. package/dist/plus/integrations/providers/azure/models.js +39 -19
  22. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  23. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +19 -0
  24. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +32 -0
  25. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -0
  26. package/dist/plus/integrations/providers/azureDevOps.js +5 -5
  27. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  28. package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
  29. package/dist/plus/integrations/providers/jira-server.js +11 -5
  30. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  31. package/dist/plus/integrations/providers/jira.d.ts +5 -2
  32. package/dist/plus/integrations/providers/jira.js +45 -30
  33. package/dist/plus/integrations/providers/jira.js.map +1 -1
  34. package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +3 -4
  35. package/dist/plus/integrations/providers/jiraIssueByKey.js +3 -4
  36. package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -1
  37. package/dist/plus/integrations/providers/linearIssuesEtag.d.ts +5 -5
  38. package/dist/plus/integrations/providers/linearIssuesEtag.js +6 -6
  39. package/dist/plus/integrations/providers/linearIssuesEtag.js.map +1 -1
  40. package/dist/plus/integrations/providers/models.d.ts +11 -1
  41. package/dist/plus/integrations/providers/models.js +9 -9
  42. package/dist/plus/integrations/providers/models.js.map +1 -1
  43. package/dist/plus/integrations/providers/providersApi.d.ts +30 -32
  44. package/dist/plus/integrations/providers/providersApi.js +149 -46
  45. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  46. package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +27 -15
  47. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +72 -10
  48. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
  49. package/dist/plus/integrations/reads/batchEtags.d.ts +5 -2
  50. package/dist/plus/integrations/reads/batchEtags.js +31 -10
  51. package/dist/plus/integrations/reads/batchEtags.js.map +1 -1
  52. package/dist/plus/integrations/reads/cursors.js +1 -1
  53. package/dist/plus/integrations/reads/drains.d.ts +0 -1
  54. package/dist/plus/integrations/reads/hierarchy.js +2 -2
  55. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  56. package/dist/plus/integrations/reads/hierarchy.utils.d.ts +13 -0
  57. package/dist/plus/integrations/reads/hierarchy.utils.js +26 -1
  58. package/dist/plus/integrations/reads/hierarchy.utils.js.map +1 -1
  59. package/dist/plus/integrations/reads/issueTracker.js +4 -4
  60. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  61. package/dist/plus/integrations/reads/issues.js +1 -1
  62. package/dist/plus/integrations/reads/pullRequestBranches.js +10 -3
  63. package/dist/plus/integrations/reads/pullRequestBranches.js.map +1 -1
  64. package/dist/plus/integrations/results.d.ts +28 -3
  65. package/dist/plus/integrations/results.js +22 -3
  66. package/dist/plus/integrations/results.js.map +1 -1
  67. package/dist/plus/integrations/utils/integration.utils.d.ts +6 -1
  68. package/dist/plus/integrations/utils/integration.utils.js +8 -1
  69. package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
  70. package/docs/integrations.md +54 -19
  71. package/package.json +2 -2
@@ -6,8 +6,8 @@ import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, Integration
6
6
  import { ProviderOrganization } from "../results.js";
7
7
  import { ProviderPullRequestCount, ProviderPullRequestSearchPage } from "../models/pullRequestReads.js";
8
8
  import { IssueFilter } from "../providerFilters.js";
9
- import { GetIssuesOptions, GetPullRequestsForUserOptions, GetPullRequestsOptions, GetReposOptions, PageInfo, PagingInput, PagingMode, ProviderAccount, ProviderApiCollectionResult, ProviderApiPagedResult, ProviderAzureProject, ProviderAzureResource, ProviderBitbucketResource, ProviderGitHubOrganization, ProviderGitLabGroup, ProviderHierarchyResult, ProviderIssue, ProviderJiraProject, ProviderJiraResource, ProviderJiraServerProject, ProviderLinearOrganization, ProviderLinearTeam, ProviderPullRequest, ProviderRepoInput, ProviderReposInput, ProviderRepository, ProviderRequestResponse } from "./models.js";
10
- import { AzurePullRequest, AzureWorkItemResponse } from "./azure/models.js";
9
+ import { GetIssuesOptions, GetPullRequestsForUserOptions, GetPullRequestsOptions, GetReposOptions, JiraIssuePage, PageInfo, PagingInput, PagingMode, ProviderAccount, ProviderApiCollectionResult, ProviderApiPagedResult, ProviderAzureProject, ProviderAzureResource, ProviderBitbucketResource, ProviderGitHubOrganization, ProviderGitLabGroup, ProviderHierarchyResult, ProviderIssue, ProviderJiraProject, ProviderJiraResource, ProviderJiraServerProject, ProviderLinearOrganization, ProviderLinearTeam, ProviderPullRequest, ProviderRepoInput, ProviderReposInput, ProviderRepository, ProviderRequestResponse } from "./models.js";
10
+ import { AzurePullRequest, AzureWorkItemEtagRead } from "./azure/models.js";
11
11
  import { countBitbucketServerPullRequests, searchBitbucketServerPullRequestsPage } from "./bitbucket-server/pullRequestSearch.js";
12
12
  import { JiraIssueEtagResponse } from "./jiraIssueByKey.js";
13
13
  import { LinearIssueEtagNode } from "./linearIssuesEtag.js";
@@ -18,6 +18,12 @@ export declare class ProvidersApi {
18
18
  private readonly authenticationService;
19
19
  private readonly providers;
20
20
  private readonly request;
21
+ /**
22
+ * Each work item type's states, by URL (host, organization, project and type) and credential: coalesces the reads
23
+ * of the same type, and never answers one credential with what another one read. A failed read is not kept, so the
24
+ * next read retries it.
25
+ */
26
+ private readonly azureWorkItemTypeStates;
21
27
  constructor(authenticationService: IntegrationAuthenticationService);
22
28
  getScopesForProvider(providerId: IntegrationIds): string[] | undefined;
23
29
  getProviderDomain(providerId: IntegrationIds): string | undefined;
@@ -122,8 +128,9 @@ export declare class ProvidersApi {
122
128
  * request, where {@link getAzureWorkItem} reads one work item per request, selecting only
123
129
  * `azureWorkItemEtagFieldNames`. Asked with `errorPolicy=omit`, without which one missing id fails the whole
124
130
  * request; Azure then leaves out every id it can't return, for whatever reason, so a caller matches the work items
125
- * to its ids by `id`, never by position, and reads an omission as unknown rather than absent. Every failure throws,
126
- * classified as {@link getAzureWorkItem} classifies it.
131
+ * to its ids by `id`, never by position, and reads an omission as unknown rather than absent. Each work item comes
132
+ * with the workflow category its state classifies to, as {@link getAzureWorkItem} classifies it. Every failure
133
+ * throws, classified as {@link getAzureWorkItem} classifies it, the states reads' included.
127
134
  */
128
135
  getAzureWorkItemsEtagFields(tokenOptInfo: TokenOptInfo, scope: {
129
136
  namespace: string;
@@ -131,7 +138,7 @@ export declare class ProvidersApi {
131
138
  }, ids: readonly number[], options: {
132
139
  isPAT?: boolean;
133
140
  baseUrl?: string;
134
- }): Promise<AzureWorkItemResponse[]>;
141
+ }): Promise<AzureWorkItemEtagRead[]>;
135
142
  /**
136
143
  * The cheap check behind the batch pull request read's etags: one pull request as Azure DevOps returns it, by the
137
144
  * same request {@link getPullRequestForRepo} has provider-apis send, but without the repository read it adds for
@@ -341,18 +348,25 @@ export declare class ProvidersApi {
341
348
  getIssuesForAzureProject(tokenOptInfo: TokenOptInfo<GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer>, namespace: string, project: string, options?: GetIssuesOptions & {
342
349
  isPAT?: boolean;
343
350
  baseUrl?: string;
344
- }): Promise<PagedResult<ProviderIssue>>;
351
+ }): Promise<ProviderApiPagedResult<ProviderIssue>>;
352
+ /**
353
+ * Gives each of `issues` with both a type and a state the workflow category of that state, from its type's states
354
+ * (see {@link azureWorkItemTypeStates}): `undefined` for a state the type doesn't list. Keyed on the raw
355
+ * `System.State`, which is the state name on every Azure read here because core passes the SDK no state-name map;
356
+ * passing one would break the lookup.
357
+ *
358
+ * Reads each distinct type once, a bounded number at a time. A type whose read fails leaves its issues untouched,
359
+ * after the other types' issues are classified, and the first failure then rejects, classified by
360
+ * {@link handleProviderError}.
361
+ */
362
+ private classifyAzureIssueStates;
345
363
  getIssuesForProject(tokenOptInfo: TokenWithInfo, project: string, resourceId: string, options?: GetIssuesOptions): Promise<ProviderIssue[] | undefined>;
346
364
  /**
347
365
  * Single page of {@link getIssuesForProject} that preserves the SDK's `pageInfo` so a caller can drain
348
366
  * every page (the plain {@link getIssuesForProject} discards it, silently capping at the first page).
349
367
  * `nextCursor` is the raw provider cursor (Jira offset / nextPageToken) fed back verbatim as `options.cursor`.
350
368
  */
351
- getIssuesForProjectPaged(tokenOptInfo: TokenWithInfo, project: string, resourceId: string, options?: GetIssuesOptions): Promise<{
352
- data: ProviderIssue[];
353
- hasMore: boolean;
354
- nextCursor: string | undefined;
355
- } | undefined>;
369
+ getIssuesForProjectPaged(tokenOptInfo: TokenWithInfo, project: string, resourceId: string, options?: GetIssuesOptions): Promise<JiraIssuePage>;
356
370
  getJiraServerCurrentUser(tokenOptInfo: TokenWithInfo<IssuesSelfManagedHostIntegrationId.JiraServer>, baseUrl: string): Promise<ProviderAccount | undefined>;
357
371
  /**
358
372
  * Jira Server's project list, which is a single unpaged `/rest/api/2/project` read rather than Cloud's
@@ -363,42 +377,26 @@ export declare class ProvidersApi {
363
377
  * One page of a Jira Server project's issues, preserving the SDK's `pageInfo` so the integration can drain
364
378
  * every page — the same contract as {@link getIssuesForProjectPaged} on Cloud.
365
379
  */
366
- getJiraServerIssuesForProjectPaged(tokenOptInfo: TokenWithInfo<IssuesSelfManagedHostIntegrationId.JiraServer>, baseUrl: string, projectKey: string, options?: GetIssuesOptions): Promise<{
367
- data: ProviderIssue[];
368
- hasMore: boolean;
369
- nextCursor: string | undefined;
370
- } | undefined>;
380
+ getJiraServerIssuesForProjectPaged(tokenOptInfo: TokenWithInfo<IssuesSelfManagedHostIntegrationId.JiraServer>, baseUrl: string, projectKey: string, options?: GetIssuesOptions): Promise<JiraIssuePage>;
371
381
  /**
372
382
  * Single page of one issue search across several projects of a Jira Server instance — the contract of
373
383
  * {@link getIssuesForProjectsPaged} on Cloud: the page and its cursor are global to the project set, and at
374
384
  * most `JIRA_MAX_PROJECT_KEYS_PER_REQUEST` keys are accepted per call.
375
385
  */
376
- getJiraServerIssuesForProjectsPaged(tokenOptInfo: TokenWithInfo<IssuesSelfManagedHostIntegrationId.JiraServer>, baseUrl: string, projectKeys: string[], options?: GetIssuesOptions): Promise<{
377
- data: ProviderIssue[];
378
- hasMore: boolean;
379
- nextCursor: string | undefined;
380
- } | undefined>;
386
+ getJiraServerIssuesForProjectsPaged(tokenOptInfo: TokenWithInfo<IssuesSelfManagedHostIntegrationId.JiraServer>, baseUrl: string, projectKeys: string[], options?: GetIssuesOptions): Promise<JiraIssuePage>;
381
387
  getJiraServerIssue(tokenOptInfo: TokenWithInfo<IssuesSelfManagedHostIntegrationId.JiraServer>, baseUrl: string, number: string): Promise<ProviderIssue | undefined>;
382
388
  /** The account-wide read: every issue related to the current user on this instance, no project needed. */
383
389
  getJiraServerIssuesForCurrentUser(tokenOptInfo: TokenWithInfo<IssuesSelfManagedHostIntegrationId.JiraServer>, baseUrl: string, options?: {
384
390
  cursor?: string;
385
391
  states?: GitIssueState[];
386
392
  sort?: IssueSorting;
387
- }): Promise<{
388
- data: ProviderIssue[];
389
- hasMore: boolean;
390
- nextCursor: string | undefined;
391
- } | undefined>;
393
+ }): Promise<JiraIssuePage>;
392
394
  /**
393
395
  * Single page of one issue search across several projects of the same Jira site. The page, its cursor and
394
396
  * `hasMore` are global to the whole project set, not per project; at most `JIRA_MAX_PROJECT_KEYS_PER_REQUEST`
395
397
  * keys are accepted per call (the SDK throws past it), so the caller chunks.
396
398
  */
397
- getIssuesForProjectsPaged(tokenOptInfo: TokenWithInfo, projectKeys: string[], resourceId: string, options?: GetIssuesOptions): Promise<{
398
- data: ProviderIssue[];
399
- hasMore: boolean;
400
- nextCursor: string | undefined;
401
- } | undefined>;
399
+ getIssuesForProjectsPaged(tokenOptInfo: TokenWithInfo, projectKeys: string[], resourceId: string, options?: GetIssuesOptions): Promise<JiraIssuePage>;
402
400
  getTrelloCard(tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Trello>, appKey: string, cardId: string, options?: {
403
401
  trelloBoardListsById?: Record<string, {
404
402
  name: string;
@@ -435,7 +433,7 @@ export declare class ProvidersApi {
435
433
  states?: GitIssueState[];
436
434
  isPAT?: boolean;
437
435
  baseUrl?: string;
438
- }): Promise<PagedResult<ProviderIssue>>;
436
+ }): Promise<ProviderApiPagedResult<ProviderIssue>>;
439
437
  getJiraIssueByKey(tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Jira>, resourceId: string, resourceUrl: string, key: string): Promise<ProviderIssue | undefined>;
440
438
  /**
441
439
  * The change state of up to `jiraBulkFetchMaxKeys` issues of one Jira Cloud site, in one bulk fetch. Unlike
@@ -1,13 +1,17 @@
1
+ import { mapSettledBounded } from "../../../utils/promise.js";
2
+ import { PromiseCache } from "../../../utils/promiseCache.js";
1
3
  import { RequestNotFoundError } from "../../../git/errors.js";
2
4
  import { base64 } from "../../../utils/base64.js";
5
+ import { isTerminalIssueStateCategory } from "../../../git/utils/issue.utils.js";
3
6
  import { toError } from "../errors.js";
4
7
  import "../constants.js";
5
8
  import { UnexpectedHtmlResponseError, getProviderResponseBodyMessage, isAzureProviderId, isProviderIssueNotFoundError, throwProviderError } from "./providerErrors.js";
6
- import { isIncompleteCollection } from "../collectionMetadata.js";
9
+ import { isIncompleteCollection, toCollectionScopeFailure } from "../collectionMetadata.js";
7
10
  import { toTokenWithInfo } from "../authentication/models.js";
8
11
  import { collectProviderPagedResult, mergeCollectionMetadata, parsePageCursor, toPageCursor } from "./utils/providerPaging.js";
9
- import { isRepoIdsInput, providersMetadata } from "./models.js";
12
+ import { GitIssueState as GitIssueState$1, isRepoIdsInput, providersMetadata } from "./models.js";
10
13
  import { azureWorkItemEtagFieldNames, encodeAzurePathSegment, fromAzureWorkItemToProviderIssue } from "./azure/models.js";
14
+ import { getAzureWorkItemTypeStatesUrl, parseAzureWorkItemTypeStates } from "./azure/workItemStateCategories.js";
11
15
  import { requestJiraIssueByKey, requestJiraIssuesEtagFields } from "./jiraIssueByKey.js";
12
16
  import { requestLinearIssuesEtagFields } from "./linearIssuesEtag.js";
13
17
  import { requestBitbucketServerProjects, requestBitbucketServerRepositories } from "./bitbucket-server/discovery.js";
@@ -45,6 +49,21 @@ function azureAuthorization(token, isPAT) {
45
49
  return isPAT ? `Basic ${base64(`:${token}`)}` : `Bearer ${token}`;
46
50
  }
47
51
  const azureDevOpsBaseUrl = "https://dev.azure.com";
52
+ /**
53
+ * The work items of an Azure list read that are in the states it asked for. provider-apis selects them by their closed
54
+ * and resolved dates (no state asks for open ones), while a row now also closes on its workflow category: a Removed work
55
+ * item has neither date, so an open read would otherwise list it, closed. A read of closed work items is left as the
56
+ * query selected it, since a resolved date alone selects one that carries no closed date or, when its type's states
57
+ * could not be read, no category; and it can't be completed either, as the query never selects a dateless one.
58
+ */
59
+ function withRequestedAzureIssueStates(issues, states) {
60
+ if (!(!states?.length || states.includes(GitIssueState$1.Open) && !states.includes(GitIssueState$1.Closed))) return issues;
61
+ return issues.filter((i) => i.closedDate == null && !isTerminalIssueStateCategory(i.state?.category));
62
+ }
63
+ /** How long a work item type's states classify its work items before they are read again. */
64
+ const azureWorkItemTypeStatesTTL = 3e5;
65
+ /** The most work item type state reads kept, across hosts, projects, types and credentials. */
66
+ const azureWorkItemTypeStatesCapacity = 256;
48
67
  /** The most group descriptors one identity batch read resolves. */
49
68
  const azureIdentityBatchSize = 100;
50
69
  /** Azure DevOps Services serves the identity APIs from its own host, below the organization like the main one. */
@@ -103,10 +122,32 @@ function fromTrelloCard(card, trelloBoardListsById) {
103
122
  }))
104
123
  };
105
124
  }
125
+ /**
126
+ * One Jira search page as the drains read it: `nextCursor` is the raw provider cursor (Jira offset / nextPageToken)
127
+ * fed back verbatim as `options.cursor`, and `metadata` is the SDK's, kept only when it reported something.
128
+ */
129
+ function toJiraIssuePage(result) {
130
+ if (result == null) return void 0;
131
+ return {
132
+ data: result.data,
133
+ hasMore: result.pageInfo?.hasNextPage ?? false,
134
+ nextCursor: result.pageInfo?.endCursor ?? void 0,
135
+ ...result.metadata != null ? { metadata: result.metadata } : {}
136
+ };
137
+ }
106
138
  var ProvidersApi = class {
107
139
  authenticationService;
108
140
  providers;
109
141
  request;
142
+ /**
143
+ * Each work item type's states, by URL (host, organization, project and type) and credential: coalesces the reads
144
+ * of the same type, and never answers one credential with what another one read. A failed read is not kept, so the
145
+ * next read retries it.
146
+ */
147
+ azureWorkItemTypeStates = new PromiseCache({
148
+ createTTL: azureWorkItemTypeStatesTTL,
149
+ capacity: azureWorkItemTypeStatesCapacity
150
+ });
110
151
  constructor(authenticationService) {
111
152
  this.authenticationService = authenticationService;
112
153
  const http = authenticationService.ctx.http;
@@ -634,6 +675,7 @@ var ProvidersApi = class {
634
675
  if (issue == null) throw new Error(`Azure DevOps returned no readable work item ${id}`);
635
676
  const project = workItem?.fields?.["System.TeamProject"];
636
677
  if (typeof project === "string" && project.toLowerCase() !== scope.project.toLowerCase()) throw new Error(`Azure DevOps work item ${id} is in project '${project}', not '${scope.project}'`);
678
+ await this.classifyAzureIssueStates([issue], tokenWithInfo, scope.namespace, scope.project, options);
637
679
  return issue;
638
680
  }
639
681
  /**
@@ -641,8 +683,9 @@ var ProvidersApi = class {
641
683
  * request, where {@link getAzureWorkItem} reads one work item per request, selecting only
642
684
  * `azureWorkItemEtagFieldNames`. Asked with `errorPolicy=omit`, without which one missing id fails the whole
643
685
  * request; Azure then leaves out every id it can't return, for whatever reason, so a caller matches the work items
644
- * to its ids by `id`, never by position, and reads an omission as unknown rather than absent. Every failure throws,
645
- * classified as {@link getAzureWorkItem} classifies it.
686
+ * to its ids by `id`, never by position, and reads an omission as unknown rather than absent. Each work item comes
687
+ * with the workflow category its state classifies to, as {@link getAzureWorkItem} classifies it. Every failure
688
+ * throws, classified as {@link getAzureWorkItem} classifies it, the states reads' included.
646
689
  */
647
690
  async getAzureWorkItemsEtagFields(tokenOptInfo, scope, ids, options) {
648
691
  const { tokenWithInfo } = await this.ensureProviderToken(tokenOptInfo);
@@ -656,7 +699,20 @@ var ProvidersApi = class {
656
699
  headers: { Authorization: options.isPAT ? `Basic ${base64(`:${token}`)}` : `Bearer ${token}` }
657
700
  })).body?.value;
658
701
  if (!Array.isArray(workItems)) throw new Error("Azure DevOps returned no work items");
659
- return workItems.filter((w) => w != null);
702
+ const readable = workItems.filter((w) => w != null);
703
+ const issues = readable.map((w) => {
704
+ const state = w.fields?.["System.State"];
705
+ const type = w.fields?.["System.WorkItemType"];
706
+ return {
707
+ type: typeof type === "string" ? type : void 0,
708
+ state: typeof state === "string" && state ? { name: state } : void 0
709
+ };
710
+ });
711
+ await this.classifyAzureIssueStates(issues, tokenWithInfo, scope.namespace, scope.project, options);
712
+ return readable.map((w, i) => ({
713
+ workItem: w,
714
+ category: issues[i].state?.category
715
+ }));
660
716
  } catch (e) {
661
717
  return this.handleProviderError(tokenWithInfo, e);
662
718
  }
@@ -1088,11 +1144,88 @@ var ProvidersApi = class {
1088
1144
  }
1089
1145
  async getIssuesForAzureProject(tokenOptInfo, namespace, project, options) {
1090
1146
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getIssuesForAzureProjectFn");
1091
- return this.getPagedResult({
1147
+ const result = await this.getPagedResult({
1092
1148
  namespace,
1093
1149
  project,
1094
1150
  ...options
1095
1151
  }, provider.getIssuesForAzureProjectFn, tokenWithInfo, options?.cursor, options?.isPAT, options?.baseUrl);
1152
+ try {
1153
+ await this.classifyAzureIssueStates(result.values, tokenWithInfo, namespace, project, options);
1154
+ } catch (ex) {
1155
+ return {
1156
+ ...result,
1157
+ values: withRequestedAzureIssueStates(result.values, options?.states),
1158
+ paging: {
1159
+ ...result.paging ?? {
1160
+ cursor: "{}",
1161
+ more: false
1162
+ },
1163
+ truncated: true
1164
+ },
1165
+ metadata: mergeCollectionMetadata(result.metadata, {
1166
+ completeness: "partial",
1167
+ failures: [toCollectionScopeFailure({
1168
+ providerId: tokenWithInfo.providerId,
1169
+ resourceId: namespace,
1170
+ projectId: project
1171
+ }, ex)]
1172
+ })
1173
+ };
1174
+ }
1175
+ return {
1176
+ ...result,
1177
+ values: withRequestedAzureIssueStates(result.values, options?.states)
1178
+ };
1179
+ }
1180
+ /**
1181
+ * Gives each of `issues` with both a type and a state the workflow category of that state, from its type's states
1182
+ * (see {@link azureWorkItemTypeStates}): `undefined` for a state the type doesn't list. Keyed on the raw
1183
+ * `System.State`, which is the state name on every Azure read here because core passes the SDK no state-name map;
1184
+ * passing one would break the lookup.
1185
+ *
1186
+ * Reads each distinct type once, a bounded number at a time. A type whose read fails leaves its issues untouched,
1187
+ * after the other types' issues are classified, and the first failure then rejects, classified by
1188
+ * {@link handleProviderError}.
1189
+ */
1190
+ async classifyAzureIssueStates(issues, tokenWithInfo, namespace, project, options) {
1191
+ const baseUrl = options?.baseUrl ?? azureDevOpsBaseUrl;
1192
+ const types = [...new Set(issues.flatMap((i) => i.state?.name && i.type ? [i.type] : []))];
1193
+ if (!types.length) return;
1194
+ const token = tokenWithInfo.accessToken;
1195
+ const results = await mapSettledBounded(types, 6, async (type) => {
1196
+ const url = getAzureWorkItemTypeStatesUrl(baseUrl, namespace, project, type);
1197
+ return [type, await this.azureWorkItemTypeStates.getOrCreate(JSON.stringify([
1198
+ baseUrl.replace(/\/$/, ""),
1199
+ namespace.toLowerCase(),
1200
+ project.toLowerCase(),
1201
+ type.toLowerCase(),
1202
+ token,
1203
+ options?.isPAT === true
1204
+ ]), async () => {
1205
+ try {
1206
+ const response = await this.request({
1207
+ url,
1208
+ headers: { Authorization: azureAuthorization(token, options?.isPAT) }
1209
+ });
1210
+ return parseAzureWorkItemTypeStates(response.body);
1211
+ } catch (ex) {
1212
+ return this.handleProviderError(tokenWithInfo, ex);
1213
+ }
1214
+ })];
1215
+ });
1216
+ const statesByType = /* @__PURE__ */ new Map();
1217
+ let failure;
1218
+ for (const result of results) if (result.status === "fulfilled") statesByType.set(result.value[0], result.value[1]);
1219
+ else failure ??= result;
1220
+ for (const issue of issues) {
1221
+ const states = issue.type ? statesByType.get(issue.type) : void 0;
1222
+ if (states == null || issue.state == null) continue;
1223
+ issue.state = {
1224
+ ...issue.state,
1225
+ category: states.get(issue.state.name)
1226
+ };
1227
+ }
1228
+ if (failure != null) throw failure.reason;
1096
1229
  }
1097
1230
  async getIssuesForProject(tokenOptInfo, project, resourceId, options) {
1098
1231
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getIssuesForProjectFn");
@@ -1117,18 +1250,12 @@ var ProvidersApi = class {
1117
1250
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getIssuesForProjectFn");
1118
1251
  const token = tokenWithInfo.accessToken;
1119
1252
  try {
1120
- const result = await provider.getIssuesForProjectFn?.({
1253
+ return toJiraIssuePage(await provider.getIssuesForProjectFn?.({
1121
1254
  projectKey: project,
1122
1255
  resourceId,
1123
1256
  ...options,
1124
1257
  includeTransitions: jiraListIncludeTransitions
1125
- }, { token });
1126
- if (result == null) return void 0;
1127
- return {
1128
- data: result.data,
1129
- hasMore: result.pageInfo?.hasNextPage ?? false,
1130
- nextCursor: result.pageInfo?.endCursor ?? void 0
1131
- };
1258
+ }, { token }));
1132
1259
  } catch (e) {
1133
1260
  return this.handleProviderError(tokenWithInfo, e);
1134
1261
  }
@@ -1166,20 +1293,14 @@ var ProvidersApi = class {
1166
1293
  async getJiraServerIssuesForProjectPaged(tokenOptInfo, baseUrl, projectKey, options) {
1167
1294
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getJiraServerIssuesForProjectFn");
1168
1295
  try {
1169
- const result = await provider.getJiraServerIssuesForProjectFn?.({
1296
+ return toJiraIssuePage(await provider.getJiraServerIssuesForProjectFn?.({
1170
1297
  projectKey,
1171
1298
  ...options,
1172
1299
  includeTransitions: jiraListIncludeTransitions
1173
1300
  }, {
1174
1301
  token: tokenWithInfo.accessToken,
1175
1302
  baseUrl
1176
- });
1177
- if (result == null) return void 0;
1178
- return {
1179
- data: result.data,
1180
- hasMore: result.pageInfo?.hasNextPage ?? false,
1181
- nextCursor: result.pageInfo?.endCursor ?? void 0
1182
- };
1303
+ }));
1183
1304
  } catch (e) {
1184
1305
  return this.handleProviderError(tokenWithInfo, e);
1185
1306
  }
@@ -1192,20 +1313,14 @@ var ProvidersApi = class {
1192
1313
  async getJiraServerIssuesForProjectsPaged(tokenOptInfo, baseUrl, projectKeys, options) {
1193
1314
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getJiraServerIssuesForProjectsFn");
1194
1315
  try {
1195
- const result = await provider.getJiraServerIssuesForProjectsFn?.({
1316
+ return toJiraIssuePage(await provider.getJiraServerIssuesForProjectsFn?.({
1196
1317
  projectKeys,
1197
1318
  ...options,
1198
1319
  includeTransitions: jiraListIncludeTransitions
1199
1320
  }, {
1200
1321
  token: tokenWithInfo.accessToken,
1201
1322
  baseUrl
1202
- });
1203
- if (result == null) return void 0;
1204
- return {
1205
- data: result.data,
1206
- hasMore: result.pageInfo?.hasNextPage ?? false,
1207
- nextCursor: result.pageInfo?.endCursor ?? void 0
1208
- };
1323
+ }));
1209
1324
  } catch (e) {
1210
1325
  return this.handleProviderError(tokenWithInfo, e);
1211
1326
  }
@@ -1226,7 +1341,7 @@ var ProvidersApi = class {
1226
1341
  async getJiraServerIssuesForCurrentUser(tokenOptInfo, baseUrl, options) {
1227
1342
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getJiraServerIssuesForCurrentUserFn");
1228
1343
  try {
1229
- const result = await provider.getJiraServerIssuesForCurrentUserFn?.({
1344
+ return toJiraIssuePage(await provider.getJiraServerIssuesForCurrentUserFn?.({
1230
1345
  cursor: options?.cursor,
1231
1346
  states: options?.states,
1232
1347
  sort: options?.sort,
@@ -1234,13 +1349,7 @@ var ProvidersApi = class {
1234
1349
  }, {
1235
1350
  token: tokenWithInfo.accessToken,
1236
1351
  baseUrl
1237
- });
1238
- if (result == null) return void 0;
1239
- return {
1240
- data: result.data,
1241
- hasMore: result.pageInfo?.hasNextPage ?? false,
1242
- nextCursor: result.pageInfo?.endCursor ?? void 0
1243
- };
1352
+ }));
1244
1353
  } catch (e) {
1245
1354
  return this.handleProviderError(tokenWithInfo, e);
1246
1355
  }
@@ -1254,18 +1363,12 @@ var ProvidersApi = class {
1254
1363
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getIssuesForProjectsFn");
1255
1364
  const token = tokenWithInfo.accessToken;
1256
1365
  try {
1257
- const result = await provider.getIssuesForProjectsFn?.({
1366
+ return toJiraIssuePage(await provider.getIssuesForProjectsFn?.({
1258
1367
  projectKeys,
1259
1368
  resourceId,
1260
1369
  ...options,
1261
1370
  includeTransitions: jiraListIncludeTransitions
1262
- }, { token });
1263
- if (result == null) return void 0;
1264
- return {
1265
- data: result.data,
1266
- hasMore: result.pageInfo?.hasNextPage ?? false,
1267
- nextCursor: result.pageInfo?.endCursor ?? void 0
1268
- };
1371
+ }, { token }));
1269
1372
  } catch (e) {
1270
1373
  return this.handleProviderError(tokenWithInfo, e);
1271
1374
  }