@gitkraken/core-gitlens 0.16.1 → 0.16.2

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 (29) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/dist/plus/integrations/integrationService.d.ts +1 -0
  3. package/dist/plus/integrations/integrationService.js +18 -5
  4. package/dist/plus/integrations/integrationService.js.map +1 -1
  5. package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
  6. package/dist/plus/integrations/providers/apiClients.js +12 -3
  7. package/dist/plus/integrations/providers/apiClients.js.map +1 -1
  8. package/dist/plus/integrations/providers/azure/azure.d.ts +13 -11
  9. package/dist/plus/integrations/providers/azure/azure.js +128 -47
  10. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  11. package/dist/plus/integrations/providers/azure/models.d.ts +4 -10
  12. package/dist/plus/integrations/providers/azure/models.js +25 -9
  13. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  14. package/dist/plus/integrations/providers/azure/workItemDescriptions.d.ts +10 -0
  15. package/dist/plus/integrations/providers/azure/workItemDescriptions.js +48 -0
  16. package/dist/plus/integrations/providers/azure/workItemDescriptions.js.map +1 -0
  17. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +20 -19
  18. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +65 -40
  19. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  20. package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
  21. package/dist/plus/integrations/providers/providerApiClient.d.ts +9 -0
  22. package/dist/plus/integrations/providers/providerApiClient.js +66 -0
  23. package/dist/plus/integrations/providers/providerApiClient.js.map +1 -0
  24. package/dist/plus/integrations/providers/providersApi.d.ts +10 -9
  25. package/dist/plus/integrations/providers/providersApi.js +163 -145
  26. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  27. package/dist/plus/integrations/reads/drains.d.ts +0 -1
  28. package/docs/integrations.md +30 -13
  29. package/package.json +2 -2
@@ -1,5 +1,4 @@
1
1
  import { IssueSorting } from "../../../git/models/issue.js";
2
- import "../providerFilters.js";
3
2
  //#region ../plus/integrations/src/providers/issueSorts.d.ts
4
3
  /** GitHub and GHE: one `search` channel serves all three issue reads, so all three order the same way. */
5
4
  export declare const githubIssueSorts: IssueSorting[];
@@ -0,0 +1,9 @@
1
+ import { ProviderRequestFunction, ProviderRequestResponse } from "./models.js";
2
+ import ProviderApis from "@gitkraken/provider-apis";
3
+ //#region ../plus/integrations/src/providers/providerApiClient.d.ts
4
+ type ProviderApisFactory = typeof ProviderApis;
5
+ export declare const createProviderApis: ProviderApisFactory;
6
+ export declare function createProviderApiRequest(fetch: (input: string | URL, init?: RequestInit) => Promise<Response>, userAgent?: string): ProviderRequestFunction;
7
+ export declare function parseFetchResponseForApi<T>(response: Response, sentCredential?: () => boolean): Promise<ProviderRequestResponse<T>>;
8
+ //#endregion
9
+ //# sourceMappingURL=providerApiClient.d.ts.map
@@ -0,0 +1,66 @@
1
+ import { getAuthenticatedUser } from "../errors.js";
2
+ import { AnonymousResponseError, UnexpectedHtmlResponseError, getProviderResponseBodyMessage, markRequestError } from "./providerErrors.js";
3
+ import ProviderApis from "@gitkraken/provider-apis";
4
+ //#region ../plus/integrations/src/providers/providerApiClient.ts
5
+ const createProviderApis = ProviderApis.default ?? ProviderApis;
6
+ function createProviderApiRequest(fetch, userAgent) {
7
+ return async ({ url, ...options }) => {
8
+ try {
9
+ return await parseFetchResponseForApi(await fetch(url, {
10
+ ...options,
11
+ headers: {
12
+ ...userAgent != null ? { "User-Agent": userAgent } : {},
13
+ ...options.headers
14
+ }
15
+ }), () => hasAuthorization(options.headers));
16
+ } catch (ex) {
17
+ throw markRequestError(ex);
18
+ }
19
+ };
20
+ }
21
+ async function parseFetchResponseForApi(response, sentCredential) {
22
+ const contentType = (response.headers.get("content-type") || "").toLowerCase();
23
+ if (response.status !== 401 && response.status !== 429 && response.status < 500 && getAuthenticatedUser(response.headers) === "anonymous" && sentCredential?.() === true) {
24
+ response.body?.cancel().catch(() => void 0);
25
+ throw new AnonymousResponseError(response.status, {
26
+ headers: Object.fromEntries(response.headers.entries()),
27
+ status: response.status
28
+ });
29
+ }
30
+ let body;
31
+ let servedHtml = false;
32
+ if (contentType.startsWith("application/json")) {
33
+ const text = await response.text();
34
+ if (text.trimStart().startsWith("<")) {
35
+ servedHtml = true;
36
+ body = text;
37
+ } else body = text.trim().length > 0 ? JSON.parse(text) : null;
38
+ } else if (contentType.startsWith("text/") || contentType === "") {
39
+ body = await response.text();
40
+ servedHtml = contentType.startsWith("text/html") && body.trim().length > 0;
41
+ } else if (contentType.startsWith("application/vnd.github.raw+json")) body = await response.arrayBuffer();
42
+ else throw new Error(`Unsupported content-type: ${contentType}`);
43
+ const result = {
44
+ body,
45
+ headers: Object.fromEntries(response.headers.entries()),
46
+ status: response.status,
47
+ statusText: response.statusText
48
+ };
49
+ if (response.ok && servedHtml) throw new UnexpectedHtmlResponseError(response.status, contentType, result);
50
+ if (!response.ok) {
51
+ const status = `(${response.status})${response.statusText ? ` ${response.statusText}` : ""}.`;
52
+ const detail = getProviderResponseBodyMessage(body);
53
+ const error = new Error(detail != null ? `${status} ${detail}` : status);
54
+ Object.assign(error, { response: result });
55
+ throw error;
56
+ }
57
+ return result;
58
+ }
59
+ function hasAuthorization(headers) {
60
+ if (headers == null) return false;
61
+ return Object.entries(headers).some(([name, value]) => name.toLowerCase() === "authorization" && Boolean(value));
62
+ }
63
+ //#endregion
64
+ export { createProviderApiRequest, createProviderApis, parseFetchResponseForApi };
65
+
66
+ //# sourceMappingURL=providerApiClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providerApiClient.js","names":[],"sources":["../../../../../plus/integrations/src/providers/providerApiClient.ts"],"sourcesContent":["import ProviderApis from '@gitkraken/provider-apis';\nimport { getAuthenticatedUser } from '../errors.js';\nimport type { ProviderRequestFunction, ProviderRequestOptions, ProviderRequestResponse } from './models.js';\nimport {\n\tAnonymousResponseError,\n\tgetProviderResponseBodyMessage,\n\tmarkRequestError,\n\tUnexpectedHtmlResponseError,\n} from './providerErrors.js';\n\n// `@gitkraken/provider-apis` is published as CommonJS with its factory on the `default` export.\n// How that surfaces depends on the consuming bundler's CJS->ESM interop: esbuild yields the\n// callable factory directly, while webpack and Node surface the module namespace (with the\n// factory under `.default`). Normalize to the callable factory so every consumer resolves it.\ntype ProviderApisFactory = typeof ProviderApis;\nexport const createProviderApis: ProviderApisFactory =\n\t(ProviderApis as ProviderApisFactory & { default?: ProviderApisFactory }).default ?? ProviderApis;\n\nexport function createProviderApiRequest(\n\tfetch: (input: string | URL, init?: RequestInit) => Promise<Response>,\n\tuserAgent?: string,\n): ProviderRequestFunction {\n\treturn async <T>({ url, ...options }: ProviderRequestOptions): Promise<ProviderRequestResponse<T>> => {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\t...options,\n\t\t\t\theaders: { ...(userAgent != null ? { 'User-Agent': userAgent } : {}), ...options.headers },\n\t\t\t});\n\t\t\treturn await parseFetchResponseForApi<T>(response, () => hasAuthorization(options.headers));\n\t\t} catch (ex) {\n\t\t\tthrow markRequestError(ex);\n\t\t}\n\t};\n}\n\nexport async function parseFetchResponseForApi<T>(\n\tresponse: Response,\n\tsentCredential?: () => boolean,\n): Promise<ProviderRequestResponse<T>> {\n\t// Media types are case-insensitive (RFC 9110 §8.3.1) and `fetch` hands the header back exactly as the server\n\t// wrote it, so match on a lowercased copy rather than the raw value.\n\tconst contentType = (response.headers.get('content-type') || '').toLowerCase();\n\n\t// A credential the server served as the anonymous user was rejected, whatever the body says; reading it as data\n\t// reports an account with nothing in it (see `AnonymousResponseError`). Checked before the body is parsed, which\n\t// may itself fail. A 401 already fails as the credential's rejection, with its body, and a throttle or a server\n\t// failure keeps its own error: neither proves anything about the credential.\n\tif (\n\t\tresponse.status !== 401 &&\n\t\tresponse.status !== 429 &&\n\t\tresponse.status < 500 &&\n\t\tgetAuthenticatedUser(response.headers) === 'anonymous' &&\n\t\tsentCredential?.() === true\n\t) {\n\t\t// Released rather than left for the collector: an unread body holds its connection.\n\t\tvoid response.body?.cancel().catch(() => undefined);\n\t\tthrow new AnonymousResponseError(response.status, {\n\t\t\theaders: Object.fromEntries(response.headers.entries()),\n\t\t\tstatus: response.status,\n\t\t});\n\t}\n\n\tlet body;\n\tlet servedHtml = false;\n\n\t// parse the response body\n\tif (contentType.startsWith('application/json')) {\n\t\tconst text = await response.text();\n\t\t// A sign-in page a server mislabels as JSON is the same page under another header, and `<` can never open\n\t\t// valid JSON — so recognize it rather than letting JSON.parse throw a SyntaxError carrying no status.\n\t\tif (text.trimStart().startsWith('<')) {\n\t\t\tservedHtml = true;\n\t\t\tbody = text;\n\t\t} else {\n\t\t\tbody = text.trim().length > 0 ? JSON.parse(text) : null;\n\t\t}\n\t} else if (contentType.startsWith('text/') || contentType === '') {\n\t\tbody = await response.text();\n\t\t// An empty body is never a sign-in page, and a write answering `204` can carry a stale `text/html` from\n\t\t// whatever its endpoint usually returns — so require actual markup, not just the header.\n\t\tservedHtml = contentType.startsWith('text/html') && body.trim().length > 0;\n\t} else if (contentType.startsWith('application/vnd.github.raw+json')) {\n\t\tbody = await response.arrayBuffer();\n\t} else {\n\t\tthrow new Error(`Unsupported content-type: ${contentType}`);\n\t}\n\n\tconst result = {\n\t\tbody: body,\n\t\theaders: Object.fromEntries(response.headers.entries()),\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t};\n\n\t// A 2xx carrying a page rather than data is a rejected credential wearing a success status (GKDEV-3617); a\n\t// non-2xx keeps its own status error below, which is the better diagnostic.\n\tif (response.ok && servedHtml) {\n\t\tthrow new UnexpectedHtmlResponseError(response.status, contentType, result);\n\t}\n\n\t// throw an error if the response is not ok\n\tif (!response.ok) {\n\t\tconst status = `(${response.status})${response.statusText ? ` ${response.statusText}` : ''}.`;\n\t\tconst detail = getProviderResponseBodyMessage(body);\n\t\tconst error = new Error(detail != null ? `${status} ${detail}` : status);\n\t\tObject.assign(error, { response: result });\n\t\tthrow error;\n\t}\n\n\treturn result;\n}\n\nfunction hasAuthorization(headers: ProviderRequestOptions['headers']): boolean {\n\tif (headers == null) return false;\n\n\treturn Object.entries(headers).some(([name, value]) => name.toLowerCase() === 'authorization' && Boolean(value));\n}\n"],"mappings":";;;;AAeA,MAAa,qBACX,aAAyE,WAAW;AAEtF,SAAgB,yBACf,OACA,WAC0B;CAC1B,OAAO,OAAU,EAAE,KAAK,GAAG,cAA2E;EACrG,IAAI;GAKH,OAAO,MAAM,yBAA4B,MAJlB,MAAM,KAAK;IACjC,GAAG;IACH,SAAS;KAAE,GAAI,aAAa,OAAO,EAAE,cAAc,UAAU,IAAI,CAAC;KAAI,GAAG,QAAQ;IAAQ;GAC1F,CAAC,SACwD,iBAAiB,QAAQ,OAAO,CAAC;EAC3F,SAAS,IAAI;GACZ,MAAM,iBAAiB,EAAE;EAC1B;CACD;AACD;AAEA,eAAsB,yBACrB,UACA,gBACsC;CAGtC,MAAM,eAAe,SAAS,QAAQ,IAAI,cAAc,KAAK,GAAA,CAAI,YAAY;CAM7E,IACC,SAAS,WAAW,OACpB,SAAS,WAAW,OACpB,SAAS,SAAS,OAClB,qBAAqB,SAAS,OAAO,MAAM,eAC3C,iBAAiB,MAAM,MACtB;EAED,SAAc,MAAM,OAAO,CAAC,CAAC,YAAY,KAAA,CAAS;EAClD,MAAM,IAAI,uBAAuB,SAAS,QAAQ;GACjD,SAAS,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC;GACtD,QAAQ,SAAS;EAClB,CAAC;CACF;CAEA,IAAI;CACJ,IAAI,aAAa;CAGjB,IAAI,YAAY,WAAW,kBAAkB,GAAG;EAC/C,MAAM,OAAO,MAAM,SAAS,KAAK;EAGjC,IAAI,KAAK,UAAU,CAAC,CAAC,WAAW,GAAG,GAAG;GACrC,aAAa;GACb,OAAO;EACR,OACC,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,KAAK,MAAM,IAAI,IAAI;CAErD,OAAO,IAAI,YAAY,WAAW,OAAO,KAAK,gBAAgB,IAAI;EACjE,OAAO,MAAM,SAAS,KAAK;EAG3B,aAAa,YAAY,WAAW,WAAW,KAAK,KAAK,KAAK,CAAC,CAAC,SAAS;CAC1E,OAAO,IAAI,YAAY,WAAW,iCAAiC,GAClE,OAAO,MAAM,SAAS,YAAY;MAElC,MAAM,IAAI,MAAM,6BAA6B,aAAa;CAG3D,MAAM,SAAS;EACR;EACN,SAAS,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC;EACtD,QAAQ,SAAS;EACjB,YAAY,SAAS;CACtB;CAIA,IAAI,SAAS,MAAM,YAClB,MAAM,IAAI,4BAA4B,SAAS,QAAQ,aAAa,MAAM;CAI3E,IAAI,CAAC,SAAS,IAAI;EACjB,MAAM,SAAS,IAAI,SAAS,OAAO,GAAG,SAAS,aAAa,IAAI,SAAS,eAAe,GAAG;EAC3F,MAAM,SAAS,+BAA+B,IAAI;EAClD,MAAM,QAAQ,IAAI,MAAM,UAAU,OAAO,GAAG,OAAO,GAAG,WAAW,MAAM;EACvE,OAAO,OAAO,OAAO,EAAE,UAAU,OAAO,CAAC;EACzC,MAAM;CACP;CAEA,OAAO;AACR;AAEA,SAAS,iBAAiB,SAAqD;CAC9E,IAAI,WAAW,MAAM,OAAO;CAE5B,OAAO,OAAO,QAAQ,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,WAAW,KAAK,YAAY,MAAM,mBAAmB,QAAQ,KAAK,CAAC;AAChH"}
@@ -1,3 +1,4 @@
1
+ import { Disposable } from "../../../utils/disposable.js";
1
2
  import { PagedResult } from "../../../utils/paging.js";
2
3
  import { IssueSorting } from "../../../git/models/issue.js";
3
4
  import { PullRequest, PullRequestFilter, PullRequestMergeMethod } from "../../../git/models/pullRequest.js";
@@ -6,21 +7,26 @@ import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, Integration
6
7
  import { ProviderOrganization } from "../results.js";
7
8
  import { ProviderPullRequestCount, ProviderPullRequestSearchPage } from "../models/pullRequestReads.js";
8
9
  import { IssueFilter } from "../providerFilters.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 { 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 } from "./models.js";
10
11
  import { AzurePullRequest, AzureWorkItemEtagRead } from "./azure/models.js";
11
12
  import { AzureWorkItemStatesCache } from "./azure/workItemStateCategories.js";
13
+ import { AzureWorkItemDescriptionsCache } from "./azure/workItemDescriptions.js";
12
14
  import { countBitbucketServerPullRequests, searchBitbucketServerPullRequestsPage } from "./bitbucket-server/pullRequestSearch.js";
13
15
  import { JiraIssueEtagResponse } from "./jiraIssueByKey.js";
14
16
  import { LinearIssueEtagNode } from "./linearIssuesEtag.js";
15
17
  import { IntegrationAuthenticationService } from "../authentication/integrationAuthenticationService.js";
16
18
  import { CollectionMetadata, GitIssueState, GitPullRequestState, TrelloBoard, TrelloList } from "@gitkraken/provider-apis";
17
19
  //#region ../plus/integrations/src/providers/providersApi.d.ts
18
- export declare class ProvidersApi {
20
+ export declare class ProvidersApi implements Disposable {
19
21
  private readonly authenticationService;
20
22
  private readonly azureWorkItemTypeStates;
23
+ private readonly azureWorkItemDescriptions;
21
24
  private readonly providers;
22
25
  private readonly request;
23
- constructor(authenticationService: IntegrationAuthenticationService, azureWorkItemTypeStates?: AzureWorkItemStatesCache);
26
+ private azureRestApi;
27
+ private readonly disposable;
28
+ constructor(authenticationService: IntegrationAuthenticationService, azureWorkItemTypeStates?: AzureWorkItemStatesCache, azureWorkItemDescriptions?: AzureWorkItemDescriptionsCache);
29
+ dispose(): void;
24
30
  getScopesForProvider(providerId: IntegrationIds): string[] | undefined;
25
31
  getProviderDomain(providerId: IntegrationIds): string | undefined;
26
32
  getProviderPullRequestsPagingMode(providerId: IntegrationIds): PagingMode | undefined;
@@ -102,6 +108,7 @@ export declare class ProvidersApi {
102
108
  isPAT?: boolean;
103
109
  baseUrl?: string;
104
110
  }): Promise<ProviderIssue | undefined>;
111
+ private getAzureRestResource;
105
112
  /**
106
113
  * One Azure DevOps work item by id, converted as provider-apis converts its list rows (see
107
114
  * {@link fromAzureWorkItemToProviderIssue}). provider-apis has no single work item read, so this asks Azure
@@ -347,11 +354,6 @@ export declare class ProvidersApi {
347
354
  }): Promise<ProviderApiPagedResult<ProviderIssue>>;
348
355
  private azureStatesScope;
349
356
  private readAzureStatesMetadata;
350
- /**
351
- * The organization's rich text fields a work item's description may come from, as provider-apis reads them for
352
- * its list rows. Best-effort: `undefined` on any failure, which leaves the description to the built-in fields
353
- * rather than failing the work item. Logged for every failure, a body that isn't a catalog included.
354
- */
355
357
  private getAzureWorkItemHtmlFields;
356
358
  private getAzureTypeStates;
357
359
  private getAzureProjectWorkItemStates;
@@ -451,6 +453,5 @@ export declare class ProvidersApi {
451
453
  baseUrl?: string;
452
454
  }): Promise<ProviderIssue | undefined>;
453
455
  }
454
- export declare function parseFetchResponseForApi<T>(response: Response, sentCredential?: () => boolean): Promise<ProviderRequestResponse<T>>;
455
456
  //#endregion
456
457
  //# sourceMappingURL=providersApi.d.ts.map
@@ -2,22 +2,23 @@ import { Logger as _logger } from "../../../utils/logger.js";
2
2
  import { mapSettledBounded } from "../../../utils/promise.js";
3
3
  import { RequestNotFoundError } from "../../../git/errors.js";
4
4
  import { base64 } from "../../../utils/base64.js";
5
- import { getAuthenticatedUser, toError } from "../errors.js";
5
+ import { toError } from "../errors.js";
6
6
  import "../constants.js";
7
- import { AnonymousResponseError, UnexpectedHtmlResponseError, getProviderResponseBodyMessage, isAzureProviderId, isProviderIssueNotFoundError, markRequestError, throwProviderError } from "./providerErrors.js";
7
+ import { isAzureProviderId, isProviderIssueNotFoundError, throwProviderError } from "./providerErrors.js";
8
8
  import { isIncompleteCollection, toCollectionScopeFailure, withValidSkippedItems } from "../collectionMetadata.js";
9
9
  import { toTokenWithInfo } from "../authentication/models.js";
10
10
  import { collectProviderPagedResult, mergeCollectionMetadata, parsePageCursor, toPageCursor } from "./utils/providerPaging.js";
11
11
  import { isRepoIdsInput, providersMetadata } from "./models.js";
12
12
  import { azureIssueStateCategory, azureWorkItemEtagFieldNames, encodeAzurePathSegment, fromAzureWorkItemToProviderIssue, toAzureWorkItemDescriptionFields } from "./azure/models.js";
13
- import { AzureWorkItemStatesCache, getAzureWorkItemFieldsPath, getAzureWorkItemTypeStatesUrl, getAzureWorkItemTypesUrl } from "./azure/workItemStateCategories.js";
13
+ import { AzureWorkItemDescriptionsCache } from "./azure/workItemDescriptions.js";
14
+ import { AzureWorkItemStatesCache, getAzureWorkItemTypeStatesUrl, getAzureWorkItemTypesUrl, requireAzureWorkItemStateCategories, resolveAzureWorkItemStateCategory } from "./azure/workItemStateCategories.js";
15
+ import { createProviderApiRequest, createProviderApis } from "./providerApiClient.js";
14
16
  import { requestJiraIssueByKey, requestJiraIssuesEtagFields } from "./jiraIssueByKey.js";
15
17
  import { requestLinearIssuesEtagFields } from "./linearIssuesEtag.js";
16
18
  import { requestBitbucketServerProjects, requestBitbucketServerRepositories } from "./bitbucket-server/discovery.js";
17
19
  import { countBitbucketServerPullRequests, searchBitbucketServerPullRequestsPage } from "./bitbucket-server/pullRequestSearch.js";
18
- import ProviderApis, { GitIssueState, needsAzureWorkItemHtmlFields } from "@gitkraken/provider-apis";
20
+ import { GitIssueState } from "@gitkraken/provider-apis";
19
21
  //#region ../plus/integrations/src/providers/providersApi.ts
20
- const createProviderApis = ProviderApis.default ?? ProviderApis;
21
22
  const repoNotFoundMessage = /^Repository .+ not found$/i;
22
23
  function isGraphQLErrors(ex) {
23
24
  return ex instanceof Error && Array.isArray(ex.graphQLErrors);
@@ -120,31 +121,41 @@ function toJiraIssuePage(result) {
120
121
  ...result.metadata != null ? { metadata: withValidSkippedItems(result.metadata) } : {}
121
122
  };
122
123
  }
124
+ function createAzureProviderInfo(id, azure) {
125
+ return {
126
+ ...providersMetadata[id],
127
+ provider: azure,
128
+ getRepoOfProjectFn: azure.getRepo.bind(azure),
129
+ getCurrentUserFn: azure.getCurrentUser.bind(azure),
130
+ getCurrentUserForInstanceFn: azure.getCurrentUserForInstance.bind(azure),
131
+ getAzureResourcesForUserFn: id === "azureDevOps" ? azure.getOrgsForUser.bind(azure) : azure.getCollectionsForUser.bind(azure),
132
+ getAzureProjectsForResourceFn: azure.getAzureProjects.bind(azure),
133
+ getPullRequestsForReposFn: azure.getPullRequestsForRepos.bind(azure),
134
+ getPullRequestsForRepoFn: azure.getPullRequestsForRepo.bind(azure),
135
+ getPullRequestForRepoFn: azure.getPullRequestForRepo.bind(azure),
136
+ getPullRequestsForAzureProjectsFn: azure.getPullRequestsForProjects.bind(azure),
137
+ getPullRequestsForAzureProjectFn: azure.getPullRequestsForProject.bind(azure),
138
+ getIssuesForAzureProjectFn: azure.getIssuesForAzureProject.bind(azure),
139
+ getReposForAzureProjectFn: azure.getReposForAzureProject.bind(azure),
140
+ mergePullRequestFn: azure.mergePullRequest.bind(azure)
141
+ };
142
+ }
123
143
  var ProvidersApi = class {
124
144
  authenticationService;
125
145
  azureWorkItemTypeStates;
146
+ azureWorkItemDescriptions;
126
147
  providers;
127
148
  request;
128
- constructor(authenticationService, azureWorkItemTypeStates = new AzureWorkItemStatesCache()) {
149
+ azureRestApi;
150
+ disposable;
151
+ constructor(authenticationService, azureWorkItemTypeStates = new AzureWorkItemStatesCache(), azureWorkItemDescriptions = new AzureWorkItemDescriptionsCache()) {
129
152
  this.authenticationService = authenticationService;
130
153
  this.azureWorkItemTypeStates = azureWorkItemTypeStates;
154
+ this.azureWorkItemDescriptions = azureWorkItemDescriptions;
131
155
  const http = authenticationService.ctx.http;
132
- const userAgent = http.userAgent;
133
- const customFetch = async ({ url, ...options }) => {
134
- try {
135
- return await parseFetchResponseForApi(await http.fetch(url, {
136
- ...options,
137
- headers: {
138
- "User-Agent": userAgent,
139
- ...options.headers
140
- }
141
- }), () => hasAuthorization(options.headers));
142
- } catch (ex) {
143
- throw markRequestError(ex);
144
- }
145
- };
146
- this.request = customFetch;
147
- const providerApis = createProviderApis({ request: customFetch });
156
+ this.request = createProviderApiRequest((input, init) => http.fetch(input, init), http.userAgent);
157
+ const providerApis = createProviderApis({ request: this.request });
158
+ this.azureRestApi = providerApis.azureDevOps;
148
159
  this.providers = {
149
160
  ["github"]: {
150
161
  ...providersMetadata["github"],
@@ -228,40 +239,8 @@ var ProvidersApi = class {
228
239
  getPullRequestsForRepoFn: providerApis.bitbucketServer.getPullRequestsForRepo.bind(providerApis.bitbucketServer),
229
240
  mergePullRequestFn: providerApis.bitbucketServer.mergePullRequest.bind(providerApis.bitbucketServer)
230
241
  },
231
- ["azureDevOps"]: {
232
- ...providersMetadata["azureDevOps"],
233
- provider: providerApis.azureDevOps,
234
- getRepoOfProjectFn: providerApis.azureDevOps.getRepo.bind(providerApis.azureDevOps),
235
- getCurrentUserFn: providerApis.azureDevOps.getCurrentUser.bind(providerApis.azureDevOps),
236
- getCurrentUserForInstanceFn: providerApis.azureDevOps.getCurrentUserForInstance.bind(providerApis.azureDevOps),
237
- getAzureResourcesForUserFn: providerApis.azureDevOps.getOrgsForUser.bind(providerApis.azureDevOps),
238
- getAzureProjectsForResourceFn: providerApis.azureDevOps.getAzureProjects.bind(providerApis.azureDevOps),
239
- getPullRequestsForReposFn: providerApis.azureDevOps.getPullRequestsForRepos.bind(providerApis.azureDevOps),
240
- getPullRequestsForRepoFn: providerApis.azureDevOps.getPullRequestsForRepo.bind(providerApis.azureDevOps),
241
- getPullRequestForRepoFn: providerApis.azureDevOps.getPullRequestForRepo.bind(providerApis.azureDevOps),
242
- getPullRequestsForAzureProjectsFn: providerApis.azureDevOps.getPullRequestsForProjects.bind(providerApis.azureDevOps),
243
- getPullRequestsForAzureProjectFn: providerApis.azureDevOps.getPullRequestsForProject.bind(providerApis.azureDevOps),
244
- getIssuesForAzureProjectFn: providerApis.azureDevOps.getIssuesForAzureProject.bind(providerApis.azureDevOps),
245
- getReposForAzureProjectFn: providerApis.azureDevOps.getReposForAzureProject.bind(providerApis.azureDevOps),
246
- mergePullRequestFn: providerApis.azureDevOps.mergePullRequest.bind(providerApis.azureDevOps)
247
- },
248
- ["azure-devops-server"]: {
249
- ...providersMetadata["azure-devops-server"],
250
- provider: providerApis.azureDevOps,
251
- getRepoOfProjectFn: providerApis.azureDevOps.getRepo.bind(providerApis.azureDevOps),
252
- getCurrentUserFn: providerApis.azureDevOps.getCurrentUser.bind(providerApis.azureDevOps),
253
- getCurrentUserForInstanceFn: providerApis.azureDevOps.getCurrentUserForInstance.bind(providerApis.azureDevOps),
254
- getAzureResourcesForUserFn: providerApis.azureDevOps.getCollectionsForUser.bind(providerApis.azureDevOps),
255
- getAzureProjectsForResourceFn: providerApis.azureDevOps.getAzureProjects.bind(providerApis.azureDevOps),
256
- getPullRequestsForReposFn: providerApis.azureDevOps.getPullRequestsForRepos.bind(providerApis.azureDevOps),
257
- getPullRequestsForRepoFn: providerApis.azureDevOps.getPullRequestsForRepo.bind(providerApis.azureDevOps),
258
- getPullRequestForRepoFn: providerApis.azureDevOps.getPullRequestForRepo.bind(providerApis.azureDevOps),
259
- getPullRequestsForAzureProjectsFn: providerApis.azureDevOps.getPullRequestsForProjects.bind(providerApis.azureDevOps),
260
- getPullRequestsForAzureProjectFn: providerApis.azureDevOps.getPullRequestsForProject.bind(providerApis.azureDevOps),
261
- getIssuesForAzureProjectFn: providerApis.azureDevOps.getIssuesForAzureProject.bind(providerApis.azureDevOps),
262
- getReposForAzureProjectFn: providerApis.azureDevOps.getReposForAzureProject.bind(providerApis.azureDevOps),
263
- mergePullRequestFn: providerApis.azureDevOps.mergePullRequest.bind(providerApis.azureDevOps)
264
- },
242
+ ["azureDevOps"]: createAzureProviderInfo("azureDevOps", providerApis.azureDevOps),
243
+ ["azure-devops-server"]: createAzureProviderInfo("azure-devops-server", providerApis.azureDevOps),
265
244
  ["jira"]: {
266
245
  ...providersMetadata["jira"],
267
246
  provider: providerApis.jira,
@@ -305,6 +284,17 @@ var ProvidersApi = class {
305
284
  getTrelloLabelsForBoardFn: providerApis.trello.getLabelsForBoard.bind(providerApis.trello)
306
285
  }
307
286
  };
287
+ this.disposable = authenticationService.ctx.config?.onDidChange((e) => {
288
+ if (!e.httpProxy) return;
289
+ this.azureWorkItemTypeStates.clear();
290
+ this.azureWorkItemDescriptions.clear();
291
+ this.azureRestApi = createProviderApis({ request: this.request }).azureDevOps;
292
+ this.providers["azureDevOps"] = createAzureProviderInfo("azureDevOps", this.azureRestApi);
293
+ this.providers["azure-devops-server"] = createAzureProviderInfo("azure-devops-server", this.azureRestApi);
294
+ });
295
+ }
296
+ dispose() {
297
+ this.disposable?.dispose();
308
298
  }
309
299
  getScopesForProvider(providerId) {
310
300
  return this.providers[providerId]?.scopes;
@@ -559,10 +549,11 @@ var ProvidersApi = class {
559
549
  };
560
550
  let memberOf;
561
551
  try {
552
+ const resource = await this.getAzureRestResource(tokenWithInfo, namespace, "identities", options, "5.0");
562
553
  const params = new URLSearchParams({
563
554
  identityIds: userId,
564
555
  queryMembership: "Expanded",
565
- "api-version": "5.0"
556
+ "api-version": resource.apiVersion
566
557
  });
567
558
  memberOf = (await this.request({
568
559
  url: `${base}/_apis/identities?${params.toString()}`,
@@ -577,9 +568,10 @@ var ProvidersApi = class {
577
568
  for (let i = 0; i < memberOf.length; i += azureIdentityBatchSize) chunks.push(memberOf.slice(i, i + azureIdentityBatchSize));
578
569
  const batches = [];
579
570
  try {
571
+ const resource = await this.getAzureRestResource(tokenWithInfo, namespace, "identityBatch", options, "5.0-preview.1");
580
572
  for (const descriptors of chunks) {
581
573
  const batch = await this.request({
582
- url: `${base}/_apis/identitybatch?api-version=5.0-preview.1`,
574
+ url: `${base}/_apis/identitybatch?api-version=${resource.apiVersion}`,
583
575
  method: "POST",
584
576
  headers,
585
577
  body: JSON.stringify({
@@ -624,6 +616,23 @@ var ProvidersApi = class {
624
616
  return this.handleProviderError(tokenWithInfo, e);
625
617
  }
626
618
  }
619
+ async getAzureRestResource(token, namespace, resource, options, version) {
620
+ try {
621
+ encodeAzurePathSegment(namespace);
622
+ const result = await this.azureRestApi.getRestApiResource({
623
+ namespace,
624
+ resource,
625
+ version
626
+ }, {
627
+ ...options,
628
+ token: token.accessToken
629
+ });
630
+ if (result == null) throw new Error(`Azure DevOps REST resource ${resource} is unavailable`);
631
+ return result;
632
+ } catch (ex) {
633
+ return this.handleProviderError(token, ex);
634
+ }
635
+ }
627
636
  /**
628
637
  * One Azure DevOps work item by id, converted as provider-apis converts its list rows (see
629
638
  * {@link fromAzureWorkItemToProviderIssue}). provider-apis has no single work item read, so this asks Azure
@@ -635,30 +644,39 @@ var ProvidersApi = class {
635
644
  * shape, an empty response, a work item the SDK's conversion would skip, and one in another project.
636
645
  */
637
646
  async getAzureWorkItem(tokenOptInfo, scope, id, options) {
638
- const { tokenWithInfo } = await this.ensureProviderToken(tokenOptInfo);
647
+ const { tokenWithInfo: providedToken } = await this.ensureProviderToken(tokenOptInfo);
648
+ const tokenWithInfo = { ...providedToken };
639
649
  const token = tokenWithInfo.accessToken;
640
650
  const baseUrl = (options.baseUrl ?? azureDevOpsBaseUrl).replace(/\/$/, "");
651
+ const requestOptions = {
652
+ baseUrl,
653
+ isPAT: options.isPAT ?? false
654
+ };
655
+ const encodedProject = encodeAzurePathSegment(scope.project);
656
+ const resource = await this.getAzureRestResource(tokenWithInfo, scope.namespace, "workItems", requestOptions);
641
657
  const params = new URLSearchParams({
642
658
  $expand: "Links",
643
- "api-version": "6.0"
659
+ "api-version": resource.apiVersion
644
660
  });
645
- const url = `${baseUrl}/${encodeAzurePathSegment(scope.namespace)}/${encodeAzurePathSegment(scope.project)}/_apis/wit/workitems/${id}?${params.toString()}`;
661
+ const projectPath = resource.projectScoped ? `/${encodedProject}` : "";
662
+ const url = `${baseUrl}/${encodeAzurePathSegment(scope.namespace)}${projectPath}/_apis/wit/workitems/${id}?${params.toString()}`;
646
663
  let workItem;
647
664
  try {
648
665
  workItem = (await this.request({
649
666
  url,
650
- headers: { Authorization: azureAuthorization(token, options.isPAT) }
667
+ headers: { Authorization: azureAuthorization(token, requestOptions.isPAT) }
651
668
  })).body;
652
669
  } catch (e) {
653
670
  if (isAzureNotFoundResponse(e, azureWorkItemNotFoundTypeKeys)) return void 0;
654
671
  return this.handleProviderError(tokenWithInfo, e);
655
672
  }
673
+ const project = workItem?.fields?.["System.TeamProject"];
674
+ if (typeof project !== "string" || !project.trim()) throw new Error(`Azure DevOps work item ${id} has no project`);
675
+ if (project.toLowerCase() !== scope.project.toLowerCase()) throw new Error(`Azure DevOps work item ${id} is in project '${project}', not '${scope.project}'`);
656
676
  const issue = workItem != null ? fromAzureWorkItemToProviderIssue(workItem, scope.namespace, scope.project) : void 0;
657
677
  if (issue == null) throw new Error(`Azure DevOps returned no readable work item ${id}`);
658
- const project = workItem?.fields?.["System.TeamProject"];
659
- if (typeof project === "string" && project.toLowerCase() !== scope.project.toLowerCase()) throw new Error(`Azure DevOps work item ${id} is in project '${project}', not '${scope.project}'`);
660
- const [htmlFields] = await Promise.all([needsAzureWorkItemHtmlFields(workItem?.fields) ? this.getAzureWorkItemHtmlFields(tokenWithInfo, scope.namespace, options) : void 0, this.classifyAzureIssueStates([issue], tokenWithInfo, scope.namespace, scope.project, options)]);
661
- if (htmlFields != null) Object.assign(issue, toAzureWorkItemDescriptionFields(workItem?.fields, htmlFields));
678
+ const [htmlFields] = await Promise.all([this.getAzureWorkItemHtmlFields(tokenWithInfo, scope, workItem?.fields, requestOptions), this.classifyAzureIssueStates([issue], tokenWithInfo, scope.namespace, scope.project, requestOptions)]);
679
+ Object.assign(issue, toAzureWorkItemDescriptionFields(workItem?.fields, htmlFields));
662
680
  return issue;
663
681
  }
664
682
  /**
@@ -674,8 +692,11 @@ var ProvidersApi = class {
674
692
  const { tokenWithInfo } = await this.ensureProviderToken(tokenOptInfo);
675
693
  const token = tokenWithInfo.accessToken;
676
694
  const baseUrl = (options.baseUrl ?? azureDevOpsBaseUrl).replace(/\/$/, "");
677
- const query = `ids=${ids.join(",")}&fields=${azureWorkItemEtagFieldNames.join(",")}&errorPolicy=omit&api-version=6.0`;
678
- const url = `${baseUrl}/${encodeAzurePathSegment(scope.namespace)}/${encodeAzurePathSegment(scope.project)}/_apis/wit/workitems?${query}`;
695
+ const encodedProject = encodeAzurePathSegment(scope.project);
696
+ const resource = await this.getAzureRestResource(tokenWithInfo, scope.namespace, "workItems", options);
697
+ const query = `ids=${ids.join(",")}&fields=${azureWorkItemEtagFieldNames.join(",")}&errorPolicy=omit&api-version=${resource.apiVersion}`;
698
+ const projectPath = resource.projectScoped ? `/${encodedProject}` : "";
699
+ const url = `${baseUrl}/${encodeAzurePathSegment(scope.namespace)}${projectPath}/_apis/wit/workitems?${query}`;
679
700
  try {
680
701
  const workItems = (await this.request({
681
702
  url,
@@ -709,7 +730,11 @@ var ProvidersApi = class {
709
730
  async getAzurePullRequest(tokenOptInfo, repo, id, options) {
710
731
  const { tokenWithInfo } = await this.ensureProviderToken(tokenOptInfo);
711
732
  const token = tokenWithInfo.accessToken;
712
- const url = `${(options.baseUrl ?? azureDevOpsBaseUrl).replace(/\/$/, "")}/${encodeAzurePathSegment(repo.namespace)}/${encodeAzurePathSegment(repo.project)}/_apis/git/repositories/${encodeAzurePathSegment(repo.name)}/pullrequests/${id}?api-version=6.0`;
733
+ const baseUrl = (options.baseUrl ?? azureDevOpsBaseUrl).replace(/\/$/, "");
734
+ encodeAzurePathSegment(repo.project);
735
+ encodeAzurePathSegment(repo.name);
736
+ const resource = await this.getAzureRestResource(tokenWithInfo, repo.namespace, "pullRequests", options);
737
+ const url = `${baseUrl}/${encodeAzurePathSegment(repo.namespace)}/${encodeAzurePathSegment(repo.project)}/_apis/git/repositories/${encodeAzurePathSegment(repo.name)}/pullrequests/${id}?api-version=${resource.apiVersion}`;
713
738
  try {
714
739
  const result = await this.request({
715
740
  url,
@@ -1127,7 +1152,8 @@ var ProvidersApi = class {
1127
1152
  }
1128
1153
  async getIssuesForAzureProject(tokenOptInfo, namespace, project, options) {
1129
1154
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getIssuesForAzureProjectFn");
1130
- const workItemTypeStates = options?.states?.includes(GitIssueState.Open) && options.states.includes(GitIssueState.Closed) ? void 0 : await this.getAzureProjectWorkItemStates(tokenWithInfo, namespace, project, options);
1155
+ const workflow = options?.states?.includes(GitIssueState.Open) && options.states.includes(GitIssueState.Closed) ? void 0 : await this.getAzureProjectWorkItemStates(tokenWithInfo, namespace, project, options);
1156
+ const workItemTypeStates = workflow?.selection;
1131
1157
  const page = await this.getPagedResult({
1132
1158
  namespace,
1133
1159
  project,
@@ -1146,7 +1172,7 @@ var ProvidersApi = class {
1146
1172
  }
1147
1173
  };
1148
1174
  try {
1149
- await this.classifyAzureIssueStates(result.values, tokenWithInfo, namespace, project, options, workItemTypeStates);
1175
+ await this.classifyAzureIssueStates(result.values, tokenWithInfo, namespace, project, options, workflow?.types);
1150
1176
  } catch (ex) {
1151
1177
  return {
1152
1178
  ...result,
@@ -1189,22 +1215,36 @@ var ProvidersApi = class {
1189
1215
  return this.handleProviderError(token, ex);
1190
1216
  }
1191
1217
  }
1192
- /**
1193
- * The organization's rich text fields a work item's description may come from, as provider-apis reads them for
1194
- * its list rows. Best-effort: `undefined` on any failure, which leaves the description to the built-in fields
1195
- * rather than failing the work item. Logged for every failure, a body that isn't a catalog included.
1196
- */
1197
- async getAzureWorkItemHtmlFields(token, namespace, options) {
1198
- const scope = this.azureStatesScope(token, namespace, "", options);
1218
+ async getAzureWorkItemHtmlFields(tokenWithInfo, scope, fields, requestOptions) {
1219
+ const token = tokenWithInfo.accessToken;
1220
+ const baseUrl = requestOptions.baseUrl;
1199
1221
  try {
1200
- return await this.azureWorkItemTypeStates.getHtmlFields(scope, () => this.readAzureStatesMetadata(token, `${scope.baseUrl.replace(/\/$/, "")}/${getAzureWorkItemFieldsPath(namespace)}`, scope.isPAT));
1222
+ return await this.azureWorkItemDescriptions.getFields(fields, {
1223
+ baseUrl,
1224
+ namespace: scope.namespace,
1225
+ token,
1226
+ isPAT: requestOptions.isPAT
1227
+ }, async () => {
1228
+ const resource = await this.getAzureRestResource(tokenWithInfo, scope.namespace, "fields", requestOptions);
1229
+ return (await this.request({
1230
+ url: `${baseUrl}/${encodeAzurePathSegment(scope.namespace)}/_apis/wit/fields?api-version=${resource.apiVersion}`,
1231
+ headers: { Authorization: azureAuthorization(token, requestOptions.isPAT) }
1232
+ })).body;
1233
+ });
1201
1234
  } catch (ex) {
1202
- _logger.warn(`Unable to read the Azure DevOps field catalog of '${namespace}': ${String(ex)}`);
1203
- return;
1235
+ _logger.warn(`Unable to read the Azure DevOps field catalog of '${scope.namespace}': ${String(ex)}`);
1236
+ return [];
1204
1237
  }
1205
1238
  }
1206
1239
  getAzureTypeStates(token, scope, type) {
1207
- return this.azureWorkItemTypeStates.getStates(scope, type, () => this.readAzureStatesMetadata(token, getAzureWorkItemTypeStatesUrl(scope.baseUrl, scope.namespace, scope.project, type), scope.isPAT));
1240
+ return this.azureWorkItemTypeStates.getStates(scope, type, async () => {
1241
+ const resource = await this.getAzureRestResource(token, scope.namespace, "workItemTypeStates", scope);
1242
+ return this.readAzureStatesMetadata(token, getAzureWorkItemTypeStatesUrl(scope.baseUrl, scope.namespace, scope.project, type, resource.apiVersion), scope.isPAT);
1243
+ }, void 0, async () => {
1244
+ const resource = await this.getAzureRestResource(token, scope.namespace, "backlogConfiguration", scope);
1245
+ const path = `${encodeAzurePathSegment(scope.namespace)}/${encodeAzurePathSegment(scope.project)}/_apis/work/backlogconfiguration`;
1246
+ return this.readAzureStatesMetadata(token, `${scope.baseUrl.replace(/\/$/, "")}/${path}?api-version=${resource.apiVersion}`, scope.isPAT);
1247
+ });
1208
1248
  }
1209
1249
  async getAzureProjectWorkItemStates(token, namespace, project, options) {
1210
1250
  const scope = this.azureStatesScope(token, namespace, project, options);
@@ -1215,45 +1255,65 @@ var ProvidersApi = class {
1215
1255
  };
1216
1256
  let types;
1217
1257
  try {
1218
- types = await this.azureWorkItemTypeStates.getTypes(scope, () => this.readAzureStatesMetadata(token, getAzureWorkItemTypesUrl(scope.baseUrl, namespace, project), scope.isPAT));
1258
+ types = await this.azureWorkItemTypeStates.getTypes(scope, async () => {
1259
+ const resource = await this.getAzureRestResource(token, namespace, "workItemTypes", scope);
1260
+ return this.readAzureStatesMetadata(token, getAzureWorkItemTypesUrl(scope.baseUrl, namespace, project, resource.apiVersion), scope.isPAT);
1261
+ });
1219
1262
  } catch (ex) {
1220
1263
  return {
1221
- types: [],
1222
- metadata: {
1223
- completeness: "partial",
1224
- failures: [toCollectionScopeFailure(failureScope, ex)]
1225
- }
1264
+ selection: {
1265
+ types: [],
1266
+ metadata: {
1267
+ completeness: "partial",
1268
+ failures: [toCollectionScopeFailure(failureScope, ex)]
1269
+ }
1270
+ },
1271
+ types: /* @__PURE__ */ new Map()
1226
1272
  };
1227
1273
  }
1228
- const results = await mapSettledBounded(types, 6, async (type) => ({
1229
- name: type,
1230
- states: await this.getAzureTypeStates(token, scope, type)
1231
- }));
1274
+ const statesByType = new Map(types.map((type) => [type, void 0]));
1275
+ const results = await mapSettledBounded(types, 6, async (type) => {
1276
+ const states = await this.getAzureTypeStates(token, scope, type);
1277
+ statesByType.set(type, states);
1278
+ return {
1279
+ name: type,
1280
+ states: requireAzureWorkItemStateCategories(states)
1281
+ };
1282
+ });
1232
1283
  const failures = results.flatMap((r) => r.status === "rejected" ? [toCollectionScopeFailure(failureScope, r.reason)] : []);
1233
1284
  return {
1234
- types: results.map((r, i) => r.status === "fulfilled" ? r.value : { name: types[i] }),
1235
- ...failures.length ? { metadata: {
1236
- completeness: "partial",
1237
- failures
1238
- } } : {}
1285
+ selection: {
1286
+ types: results.map((r, i) => r.status === "fulfilled" ? r.value : { name: types[i] }),
1287
+ ...failures.length ? { metadata: {
1288
+ completeness: "partial",
1289
+ failures
1290
+ } } : {}
1291
+ },
1292
+ types: statesByType
1239
1293
  };
1240
1294
  }
1241
1295
  async classifyAzureIssueStates(issues, tokenWithInfo, namespace, project, options, snapshot) {
1242
1296
  const scope = this.azureStatesScope(tokenWithInfo, namespace, project, options);
1243
- const snapshotTypes = new Map(snapshot?.types.map((type) => [type.name, type.states]));
1297
+ const snapshotTypes = new Map(snapshot);
1244
1298
  const types = [...new Set(issues.flatMap((i) => i.state?.name && i.type && !snapshotTypes.has(i.type) ? [i.type] : []))];
1245
1299
  const results = await mapSettledBounded(types, 6, async (type) => [type, await this.getAzureTypeStates(tokenWithInfo, scope, type)]);
1246
1300
  const statesByType = new Map([...snapshotTypes, ...results.flatMap((r) => r.status === "fulfilled" ? [r.value] : [])]);
1301
+ let categoryError;
1247
1302
  for (const issue of issues) {
1248
1303
  const states = issue.type ? statesByType.get(issue.type) : void 0;
1249
1304
  if (states == null || issue.state == null) continue;
1250
- issue.state = {
1251
- ...issue.state,
1252
- category: azureIssueStateCategory(states.find((s) => s.name === issue.state?.name)?.category)
1253
- };
1305
+ try {
1306
+ issue.state = {
1307
+ ...issue.state,
1308
+ category: azureIssueStateCategory(resolveAzureWorkItemStateCategory(states, issue.state.name))
1309
+ };
1310
+ } catch (ex) {
1311
+ categoryError ??= toError(ex);
1312
+ }
1254
1313
  }
1255
1314
  const failure = results.find((r) => r.status === "rejected");
1256
1315
  if (failure?.status === "rejected") throw failure.reason;
1316
+ if (categoryError != null) throw categoryError;
1257
1317
  }
1258
1318
  async getIssuesForProject(tokenOptInfo, project, resourceId, options) {
1259
1319
  const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getIssuesForProjectFn");
@@ -1505,49 +1565,7 @@ var ProvidersApi = class {
1505
1565
  }
1506
1566
  }
1507
1567
  };
1508
- async function parseFetchResponseForApi(response, sentCredential) {
1509
- const contentType = (response.headers.get("content-type") || "").toLowerCase();
1510
- if (response.status !== 401 && response.status !== 429 && response.status < 500 && getAuthenticatedUser(response.headers) === "anonymous" && sentCredential?.() === true) {
1511
- response.body?.cancel().catch(() => void 0);
1512
- throw new AnonymousResponseError(response.status, {
1513
- headers: Object.fromEntries(response.headers.entries()),
1514
- status: response.status
1515
- });
1516
- }
1517
- let body;
1518
- let servedHtml = false;
1519
- if (contentType.startsWith("application/json")) {
1520
- const text = await response.text();
1521
- if (text.trimStart().startsWith("<")) {
1522
- servedHtml = true;
1523
- body = text;
1524
- } else body = text.trim().length > 0 ? JSON.parse(text) : null;
1525
- } else if (contentType.startsWith("text/") || contentType === "") {
1526
- body = await response.text();
1527
- servedHtml = contentType.startsWith("text/html") && body.trim().length > 0;
1528
- } else if (contentType.startsWith("application/vnd.github.raw+json")) body = await response.arrayBuffer();
1529
- else throw new Error(`Unsupported content-type: ${contentType}`);
1530
- const result = {
1531
- body,
1532
- headers: Object.fromEntries(response.headers.entries()),
1533
- status: response.status,
1534
- statusText: response.statusText
1535
- };
1536
- if (response.ok && servedHtml) throw new UnexpectedHtmlResponseError(response.status, contentType, result);
1537
- if (!response.ok) {
1538
- const status = `(${response.status})${response.statusText ? ` ${response.statusText}` : ""}.`;
1539
- const detail = getProviderResponseBodyMessage(body);
1540
- const error = new Error(detail != null ? `${status} ${detail}` : status);
1541
- Object.assign(error, { response: result });
1542
- throw error;
1543
- }
1544
- return result;
1545
- }
1546
- function hasAuthorization(headers) {
1547
- if (headers == null) return false;
1548
- return Object.entries(headers).some(([name, value]) => name.toLowerCase() === "authorization" && Boolean(value));
1549
- }
1550
1568
  //#endregion
1551
- export { ProvidersApi, parseFetchResponseForApi };
1569
+ export { ProvidersApi };
1552
1570
 
1553
1571
  //# sourceMappingURL=providersApi.js.map