@gitkraken/core-gitlens 0.7.2 → 0.8.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 (163) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/dist/git/cache.d.ts +18 -5
  3. package/dist/git/cache.js +26 -8
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/context.d.ts +5 -1
  6. package/dist/git/context.js.map +1 -1
  7. package/dist/git/errors.d.ts +28 -1
  8. package/dist/git/errors.js +45 -1
  9. package/dist/git/errors.js.map +1 -1
  10. package/dist/git/providers/branches.d.ts +5 -1
  11. package/dist/git/providers/commits.d.ts +24 -1
  12. package/dist/git/providers/config.d.ts +7 -1
  13. package/dist/git/providers/operations.d.ts +6 -0
  14. package/dist/git/providers/provider.d.ts +17 -0
  15. package/dist/git/providers/refs.d.ts +38 -3
  16. package/dist/git/providers/revision.d.ts +5 -1
  17. package/dist/git/providers/staging.d.ts +12 -0
  18. package/dist/git/providers/worktrees.d.ts +23 -3
  19. package/dist/git/run.types.d.ts +6 -0
  20. package/dist/git-cli/cliGitProvider.d.ts +17 -1
  21. package/dist/git-cli/cliGitProvider.js +37 -1
  22. package/dist/git-cli/cliGitProvider.js.map +1 -1
  23. package/dist/git-cli/exec/git.d.ts +12 -2
  24. package/dist/git-cli/exec/git.js +47 -16
  25. package/dist/git-cli/exec/git.js.map +1 -1
  26. package/dist/git-cli/exec/gitQueue.d.ts +4 -1
  27. package/dist/git-cli/exec/gitQueue.js +1 -1
  28. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  29. package/dist/git-cli/providers/branches.d.ts +10 -1
  30. package/dist/git-cli/providers/branches.js +15 -8
  31. package/dist/git-cli/providers/branches.js.map +1 -1
  32. package/dist/git-cli/providers/commits.d.ts +23 -1
  33. package/dist/git-cli/providers/commits.js +57 -8
  34. package/dist/git-cli/providers/commits.js.map +1 -1
  35. package/dist/git-cli/providers/config.js +2 -1
  36. package/dist/git-cli/providers/config.js.map +1 -1
  37. package/dist/git-cli/providers/operations.d.ts +1 -0
  38. package/dist/git-cli/providers/operations.js +39 -3
  39. package/dist/git-cli/providers/operations.js.map +1 -1
  40. package/dist/git-cli/providers/patch.js +20 -41
  41. package/dist/git-cli/providers/patch.js.map +1 -1
  42. package/dist/git-cli/providers/refs.d.ts +15 -3
  43. package/dist/git-cli/providers/refs.js +115 -10
  44. package/dist/git-cli/providers/refs.js.map +1 -1
  45. package/dist/git-cli/providers/revision.d.ts +3 -1
  46. package/dist/git-cli/providers/revision.js +9 -5
  47. package/dist/git-cli/providers/revision.js.map +1 -1
  48. package/dist/git-cli/providers/staging.d.ts +8 -0
  49. package/dist/git-cli/providers/staging.js +58 -6
  50. package/dist/git-cli/providers/staging.js.map +1 -1
  51. package/dist/git-cli/providers/worktrees.d.ts +31 -2
  52. package/dist/git-cli/providers/worktrees.js +75 -7
  53. package/dist/git-cli/providers/worktrees.js.map +1 -1
  54. package/dist/plus/git-github/api/github.d.ts +64 -3
  55. package/dist/plus/git-github/api/github.js +237 -24
  56. package/dist/plus/git-github/api/github.js.map +1 -1
  57. package/dist/plus/git-github/providers/github/branches.d.ts +3 -1
  58. package/dist/plus/git-github/providers/github/branches.js +2 -1
  59. package/dist/plus/git-github/providers/github/branches.js.map +1 -1
  60. package/dist/plus/git-github/providers/github/refs.d.ts +10 -3
  61. package/dist/plus/git-github/providers/github/refs.js +4 -4
  62. package/dist/plus/git-github/providers/github/refs.js.map +1 -1
  63. package/dist/plus/git-github/providers/github/revision.d.ts +3 -1
  64. package/dist/plus/git-github/providers/github/revision.js +1 -1
  65. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  66. package/dist/plus/integrations/index.d.ts +5 -2
  67. package/dist/plus/integrations/index.js.map +1 -1
  68. package/dist/plus/integrations/integrationService.d.ts +35 -9
  69. package/dist/plus/integrations/integrationService.js +16 -3
  70. package/dist/plus/integrations/integrationService.js.map +1 -1
  71. package/dist/plus/integrations/manager.d.ts +107 -37
  72. package/dist/plus/integrations/models/gitHostIntegration.d.ts +128 -13
  73. package/dist/plus/integrations/models/gitHostIntegration.js +130 -17
  74. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  75. package/dist/plus/integrations/models/integration.d.ts +51 -3
  76. package/dist/plus/integrations/models/integration.js +125 -18
  77. package/dist/plus/integrations/models/integration.js.map +1 -1
  78. package/dist/plus/integrations/models/issuesIntegration.d.ts +18 -5
  79. package/dist/plus/integrations/models/issuesIntegration.js +40 -10
  80. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  81. package/dist/plus/integrations/providers/azure/azure.d.ts +1 -1
  82. package/dist/plus/integrations/providers/azure/azure.js +13 -17
  83. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  84. package/dist/plus/integrations/providers/azure/models.d.ts +29 -0
  85. package/dist/plus/integrations/providers/azure/models.js +112 -1
  86. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  87. package/dist/plus/integrations/providers/azureDevOps.d.ts +54 -2
  88. package/dist/plus/integrations/providers/azureDevOps.js +80 -2
  89. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  90. package/dist/plus/integrations/providers/azureDevOpsServer.js +2 -1
  91. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  92. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +61 -0
  93. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +165 -23
  94. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
  95. package/dist/plus/integrations/providers/bitbucket/models.d.ts +6 -1
  96. package/dist/plus/integrations/providers/bitbucket/models.js +4 -3
  97. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
  98. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +1 -1
  99. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -1
  100. package/dist/plus/integrations/providers/bitbucket-server.d.ts +38 -1
  101. package/dist/plus/integrations/providers/bitbucket-server.js +33 -1
  102. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  103. package/dist/plus/integrations/providers/bitbucket.d.ts +36 -1
  104. package/dist/plus/integrations/providers/bitbucket.js +29 -1
  105. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  106. package/dist/plus/integrations/providers/github.d.ts +48 -4
  107. package/dist/plus/integrations/providers/github.js +101 -6
  108. package/dist/plus/integrations/providers/github.js.map +1 -1
  109. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +35 -0
  110. package/dist/plus/integrations/providers/gitlab/gitlab.js +132 -15
  111. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
  112. package/dist/plus/integrations/providers/gitlab/models.d.ts +1 -0
  113. package/dist/plus/integrations/providers/gitlab/models.js +1 -1
  114. package/dist/plus/integrations/providers/gitlab/models.js.map +1 -1
  115. package/dist/plus/integrations/providers/gitlab.d.ts +48 -1
  116. package/dist/plus/integrations/providers/gitlab.js +79 -3
  117. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  118. package/dist/plus/integrations/providers/jira-server.d.ts +3 -3
  119. package/dist/plus/integrations/providers/jira-server.js +3 -3
  120. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  121. package/dist/plus/integrations/providers/models.d.ts +12 -1
  122. package/dist/plus/integrations/providers/models.js +11 -1
  123. package/dist/plus/integrations/providers/models.js.map +1 -1
  124. package/dist/plus/integrations/providers/providerErrors.d.ts +9 -1
  125. package/dist/plus/integrations/providers/providerErrors.js +10 -1
  126. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  127. package/dist/plus/integrations/providers/providersApi.d.ts +59 -0
  128. package/dist/plus/integrations/providers/providersApi.js +136 -1
  129. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  130. package/dist/plus/integrations/providers/utils/providerPaging.d.ts +42 -0
  131. package/dist/plus/integrations/providers/utils/providerPaging.js +58 -1
  132. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  133. package/dist/plus/integrations/providers/utils.d.ts +1 -1
  134. package/dist/plus/integrations/reads/currentAccount.d.ts +35 -0
  135. package/dist/plus/integrations/reads/currentAccount.js +46 -0
  136. package/dist/plus/integrations/reads/currentAccount.js.map +1 -0
  137. package/dist/plus/integrations/reads/drains.d.ts +4 -1
  138. package/dist/plus/integrations/reads/drains.js +7 -3
  139. package/dist/plus/integrations/reads/drains.js.map +1 -1
  140. package/dist/plus/integrations/reads/issueBatch.d.ts +72 -12
  141. package/dist/plus/integrations/reads/issueBatch.js +193 -48
  142. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  143. package/dist/plus/integrations/reads/pullRequestBatch.d.ts +57 -0
  144. package/dist/plus/integrations/reads/pullRequestBatch.js +80 -0
  145. package/dist/plus/integrations/reads/pullRequestBatch.js.map +1 -0
  146. package/dist/plus/integrations/reads/pullRequestBranches.d.ts +58 -0
  147. package/dist/plus/integrations/reads/pullRequestBranches.js +111 -0
  148. package/dist/plus/integrations/reads/pullRequestBranches.js.map +1 -0
  149. package/dist/plus/integrations/reads/pullRequests.js +3 -3
  150. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  151. package/dist/plus/integrations/reads/sweeps.js +3 -3
  152. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  153. package/dist/plus/integrations/results.d.ts +5 -2
  154. package/dist/plus/integrations/results.js.map +1 -1
  155. package/dist/utils/env/node/exec.js +1 -1
  156. package/dist/utils/env/node/exec.js.map +1 -1
  157. package/docs/integrations.md +90 -36
  158. package/docs/kepler-git-write-api-parity.md +239 -0
  159. package/docs/kepler-read-api-parity.md +75 -28
  160. package/package.json +1 -1
  161. package/dist/plus/integrations/reads/trackerIssue.d.ts +0 -33
  162. package/dist/plus/integrations/reads/trackerIssue.js +0 -60
  163. package/dist/plus/integrations/reads/trackerIssue.js.map +0 -1
@@ -4,18 +4,31 @@ import { Account } from "../../../git/models/author.js";
4
4
  import { ProviderAuthenticationSession } from "../authentication/models.js";
5
5
  import { IntegrationIds } from "../constants.js";
6
6
  import { IssuesForProjectOptions, ProjectIssuesDrain } from "./issueReads.js";
7
- import { Integration, IntegrationBase, IntegrationResult, IntegrationType } from "./integration.js";
7
+ import { BatchSlot, Integration, IntegrationBase, IntegrationResult, IntegrationType } from "./integration.js";
8
8
  import { ProviderApiCollectionResult } from "../providers/models.js";
9
9
  //#region ../plus/integrations/src/models/issuesIntegration.d.ts
10
10
  export declare function isIssuesIntegration(integration: Integration): integration is IssuesIntegration;
11
11
  export declare abstract class IssuesIntegration<ID extends IntegrationIds = IntegrationIds, T extends ResourceDescriptor = ResourceDescriptor> extends IntegrationBase<ID> {
12
12
  readonly type: IntegrationType;
13
13
  get supportsIssueLookupByResourceId(): boolean;
14
- getIssueByResourceIdResult(resourceId: string, id: string, options?: {
15
- connectionId?: string;
16
- expiryOverride?: boolean | number;
14
+ /**
15
+ * Result-returning wrapper for the BATCH tracker issue read: resolves several `(resourceId, identifier)` targets
16
+ * with one {@link getProviderIssueByResourceId} request each, run with bounded concurrency. One settled slot per
17
+ * input target: `fulfilled` with `undefined` means the issue does not exist or is not visible to this token;
18
+ * `rejected` means that target could not be checked, never that it is absent.
19
+ *
20
+ * Failure isolation happens PER TARGET, mirroring `GitHostIntegration.getPullRequestsBatchResult`: the whole
21
+ * call counts as a failure against the integration's request-exception budget only when EVERY slot rejected,
22
+ * and not even then when a credential that checks out was refused by each target's own resource
23
+ * (`settleBatchRefusals`), so a batch of mostly-good targets never spends more than one strike.
24
+ *
25
+ * Uncached on purpose: the caller owns caching, as with the git hosts' batch reads.
26
+ */
27
+ getIssuesByResourceIdBatchResult(targets: readonly {
28
+ resourceId: string;
29
+ identifier: string;
17
30
  resourceUrl?: string;
18
- }): Promise<IntegrationResult<Issue | undefined>>;
31
+ }[], connectionId?: string): Promise<IntegrationResult<BatchSlot<IssueShape | undefined>[] | undefined>>;
19
32
  protected getProviderIssueByResourceId?(_session: ProviderAuthenticationSession, _resourceId: string, _id: string, _resourceUrl: string | undefined): Promise<Issue | undefined>;
20
33
  getAccountForResource(resource: T, connectionId?: string): Promise<Account | undefined>;
21
34
  /**
@@ -1,8 +1,10 @@
1
1
  import { getScopedLogger } from "../../../utils/logger.scoped.js";
2
+ import { mapSettledBounded } from "../../../utils/promise.js";
2
3
  import { trace } from "../../../utils/decorators/log.js";
3
4
  import __decorate from "../../../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
4
5
  import { gate } from "../../../utils/decorators/gate.js";
5
- import { IntegrationReadUnavailableError, toError } from "../errors.js";
6
+ import { toError } from "../errors.js";
7
+ import "../constants.js";
6
8
  import { IntegrationBase } from "./integration.js";
7
9
  //#region ../plus/integrations/src/models/issuesIntegration.ts
8
10
  function isIssuesIntegration(integration) {
@@ -13,16 +15,44 @@ var IssuesIntegration = class extends IntegrationBase {
13
15
  get supportsIssueLookupByResourceId() {
14
16
  return this.getProviderIssueByResourceId != null;
15
17
  }
16
- getIssueByResourceIdResult(resourceId, id, options) {
18
+ /**
19
+ * Result-returning wrapper for the BATCH tracker issue read: resolves several `(resourceId, identifier)` targets
20
+ * with one {@link getProviderIssueByResourceId} request each, run with bounded concurrency. One settled slot per
21
+ * input target: `fulfilled` with `undefined` means the issue does not exist or is not visible to this token;
22
+ * `rejected` means that target could not be checked, never that it is absent.
23
+ *
24
+ * Failure isolation happens PER TARGET, mirroring `GitHostIntegration.getPullRequestsBatchResult`: the whole
25
+ * call counts as a failure against the integration's request-exception budget only when EVERY slot rejected,
26
+ * and not even then when a credential that checks out was refused by each target's own resource
27
+ * (`settleBatchRefusals`), so a batch of mostly-good targets never spends more than one strike.
28
+ *
29
+ * Uncached on purpose: the caller owns caching, as with the git hosts' batch reads.
30
+ */
31
+ async getIssuesByResourceIdBatchResult(targets, connectionId) {
32
+ const scope = getScopedLogger();
33
+ const session = await this.resolveReadSession(connectionId, scope);
34
+ if (session == null) return void 0;
17
35
  const getProviderIssue = this.getProviderIssueByResourceId;
18
- if (getProviderIssue == null) return Promise.resolve({ error: new IntegrationReadUnavailableError(this.name, "does not support direct issue reads by resource id") });
19
- const { resourceUrl, ...readOptions } = options ?? {};
20
- const resource = {
21
- key: resourceId,
22
- resourceId,
23
- resourceUrl
24
- };
25
- return this.getIssueResultCore(resource, id, readOptions, (session) => getProviderIssue.call(this, session, resourceId, id, resourceUrl));
36
+ if (getProviderIssue == null) return { value: void 0 };
37
+ const start = performance.now();
38
+ try {
39
+ const slots = await mapSettledBounded(targets, 6, (t) => getProviderIssue.call(this, session, t.resourceId, t.identifier, t.resourceUrl));
40
+ const settled = await this.settleBatchRefusals(session, targets, slots);
41
+ this.resetRequestExceptionCount("getIssue");
42
+ return {
43
+ value: settled,
44
+ duration: performance.now() - start
45
+ };
46
+ } catch (ex) {
47
+ this.handleProviderException("getIssue", ex, {
48
+ scope,
49
+ connectionId
50
+ });
51
+ return {
52
+ error: toError(ex),
53
+ duration: performance.now() - start
54
+ };
55
+ }
26
56
  }
27
57
  async getAccountForResource(resource, connectionId) {
28
58
  return (await this.getAccountForResourceResult(resource, connectionId))?.value;
@@ -1 +1 @@
1
- {"version":3,"file":"issuesIntegration.js","names":[],"sources":["../../../../../plus/integrations/src/models/issuesIntegration.ts"],"sourcesContent":["import type { Account } from '@gitlens/git/models/author.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport type { IntegrationIds } from '../constants.js';\nimport { IntegrationReadUnavailableError, toError } from '../errors.js';\nimport type { ProviderApiCollectionResult } from '../providers/models.js';\nimport type { Integration, IntegrationResult, IntegrationType } from './integration.js';\nimport { IntegrationBase } from './integration.js';\nimport type { IssuesForProjectOptions, ProjectIssuesDrain } from './issueReads.js';\n\nexport function isIssuesIntegration(integration: Integration): integration is IssuesIntegration {\n\treturn integration.type === 'issues';\n}\n\nexport abstract class IssuesIntegration<\n\tID extends IntegrationIds = IntegrationIds,\n\tT extends ResourceDescriptor = ResourceDescriptor,\n> extends IntegrationBase<ID> {\n\treadonly type: IntegrationType = 'issues';\n\n\tget supportsIssueLookupByResourceId(): boolean {\n\t\treturn this.getProviderIssueByResourceId != null;\n\t}\n\n\tgetIssueByResourceIdResult(\n\t\tresourceId: string,\n\t\tid: string,\n\t\toptions?: { connectionId?: string; expiryOverride?: boolean | number; resourceUrl?: string },\n\t): Promise<IntegrationResult<Issue | undefined>> {\n\t\tconst getProviderIssue = this.getProviderIssueByResourceId;\n\t\tif (getProviderIssue == null) {\n\t\t\treturn Promise.resolve({\n\t\t\t\terror: new IntegrationReadUnavailableError(\n\t\t\t\t\tthis.name,\n\t\t\t\t\t'does not support direct issue reads by resource id',\n\t\t\t\t),\n\t\t\t});\n\t\t}\n\n\t\tconst { resourceUrl, ...readOptions } = options ?? {};\n\t\tconst resource = { key: resourceId, resourceId: resourceId, resourceUrl: resourceUrl };\n\t\treturn this.getIssueResultCore(resource, id, readOptions, session =>\n\t\t\tgetProviderIssue.call(this, session, resourceId, id, resourceUrl),\n\t\t);\n\t}\n\n\tprotected getProviderIssueByResourceId?(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_resourceId: string,\n\t\t_id: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined>;\n\n\t@trace()\n\tasync getAccountForResource(resource: T, connectionId?: string): Promise<Account | undefined> {\n\t\treturn (await this.getAccountForResourceResult(resource, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getAccountForResource}. Recovers a thrown error into `{ error }` so\n\t * callers can preserve its classification (e.g. a 401/403 → an `auth` warning that drives re-auth)\n\t * instead of collapsing every failure into an untyped `undefined`. Gated here (not on the wrapper) so\n\t * direct callers such as the ProviderBackend facade share the same dedup as `getAccountForResource`.\n\t */\n\t@gate()\n\tasync getAccountForResourceResult(\n\t\tresource: T,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<Account | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst account = await this.getProviderAccountForResource(session, resource);\n\t\t\tthis.resetRequestExceptionCount('getAccountForResource');\n\t\t\treturn { value: account };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getAccountForResource', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t): Promise<Account | undefined>;\n\n\t@trace()\n\tasync getResourcesForUser(connectionId?: string): Promise<T[] | undefined> {\n\t\treturn (await this.getResourcesForUserResult(connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getResourcesForUser}. Recovers thrown errors into `{ error }` so callers\n\t * can surface them as warnings rather than silently swallowing them to `undefined`. Gated here (not on the\n\t * wrapper) so direct callers such as the ProviderBackend facade share the same dedup as `getResourcesForUser`.\n\t */\n\t@gate()\n\tasync getResourcesForUserResult(connectionId?: string): Promise<IntegrationResult<T[] | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst resources = await this.getProviderResourcesForUser(session);\n\t\t\tthis.resetRequestExceptionCount('getResourcesForUser');\n\t\t\treturn { value: resources };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getResourcesForUser', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderResourcesForUser(session: ProviderAuthenticationSession): Promise<T[] | undefined>;\n\n\t/**\n\t * Project discovery for a set of resources, returning the SDK collection `{ values, metadata }` (completeness\n\t * + per-resource failures) so callers can warn on failed resources and set `fetchFailed` without discarding\n\t * the resources that succeeded. Thrown errors are recovered into `{ error }` for the same reason: a caller\n\t * surfaces them as a warning rather than swallowing them to `undefined`.\n\t */\n\t@trace()\n\tasync getProjectsForResourcesWithMetadataResult(\n\t\tresources: T[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProviderApiCollectionResult<T> | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst projects = await this.getProviderProjectsForResourcesWithMetadata(session, resources);\n\t\t\tawait this.confirmScopedAuthFailures(session, projects.metadata);\n\t\t\tthis.resetRequestExceptionCount('getProjectsForResources');\n\t\t\treturn { value: projects };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getProjectsForResources', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Metadata-aware provider project discovery. The default wraps the array-returning\n\t * {@link getProviderProjectsForResources} in `{ values }` with no metadata, so providers without a\n\t * fan-out completeness signal (Linear, Trello) need no change; Jira overrides it to preserve the SDK's\n\t * per-resource completeness/failures.\n\t */\n\tprotected async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<ProviderApiCollectionResult<T>> {\n\t\tconst projects = await this.getProviderProjectsForResources(session, resources);\n\t\treturn { values: projects ?? [] };\n\t}\n\n\tprotected abstract getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<T[] | undefined>;\n\n\t@trace()\n\tasync getIssuesForProject(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getIssuesForProjectResult(project, options, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getIssuesForProject}. Recovers thrown errors into `{ error }` so callers\n\t * (e.g. the ProviderBackend facade) can surface a per-provider warning instead of a silent empty read —\n\t * important for providers that throw on unsupported operations (e.g. Linear's not-implemented issue read).\n\t */\n\tasync getIssuesForProjectResult(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<IssueShape[] | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst issues = await this.getProviderIssuesForProject(session, project, options);\n\t\t\tthis.resetRequestExceptionCount('getIssuesForProject');\n\t\t\treturn { value: issues };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Truncation-aware variant of {@link getIssuesForProjectResult}. A provider that drains a project's issues\n\t * with an internal page backstop (Jira/Linear) overrides {@link getProviderIssuesForProjectWithTruncation}\n\t * to report when that backstop was hit, so the facade can surface an incomplete project read instead of\n\t * publishing it as complete. The default reports `truncated: false`.\n\t */\n\tasync getIssuesForProjectWithTruncationResult(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProjectIssuesDrain | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst result = await this.getProviderIssuesForProjectWithTruncation(session, project, options);\n\t\t\tawait this.confirmScopedAuthFailures(session, result?.metadata);\n\t\t\tthis.resetRequestExceptionCount('getIssuesForProject');\n\t\t\treturn { value: result };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined>;\n\n\t/**\n\t * Truncation-aware core of {@link getProviderIssuesForProject}. The default wraps the plain read and\n\t * reports `truncated: false`; a provider whose per-project drain is capped by a page backstop overrides\n\t * this to report incompleteness. Optional metadata lets providers surface structured per-project failures\n\t * (e.g. a page-level auth rejection) without discarding the already-fetched prefix.\n\t */\n\tprotected async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tconst values = await this.getProviderIssuesForProject(session, project, options);\n\t\tif (values == null) return undefined;\n\t\treturn { values: values, truncated: false };\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"issuesIntegration.js","names":[],"sources":["../../../../../plus/integrations/src/models/issuesIntegration.ts"],"sourcesContent":["import type { Account } from '@gitlens/git/models/author.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { mapSettledBounded } from '@gitlens/utils/promise.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport type { IntegrationIds } from '../constants.js';\nimport { providerFanOutConcurrency } from '../constants.js';\nimport { toError } from '../errors.js';\nimport type { ProviderApiCollectionResult } from '../providers/models.js';\nimport type { BatchSlot, Integration, IntegrationResult, IntegrationType } from './integration.js';\nimport { IntegrationBase } from './integration.js';\nimport type { IssuesForProjectOptions, ProjectIssuesDrain } from './issueReads.js';\n\nexport function isIssuesIntegration(integration: Integration): integration is IssuesIntegration {\n\treturn integration.type === 'issues';\n}\n\nexport abstract class IssuesIntegration<\n\tID extends IntegrationIds = IntegrationIds,\n\tT extends ResourceDescriptor = ResourceDescriptor,\n> extends IntegrationBase<ID> {\n\treadonly type: IntegrationType = 'issues';\n\n\tget supportsIssueLookupByResourceId(): boolean {\n\t\treturn this.getProviderIssueByResourceId != null;\n\t}\n\n\t/**\n\t * Result-returning wrapper for the BATCH tracker issue read: resolves several `(resourceId, identifier)` targets\n\t * with one {@link getProviderIssueByResourceId} request each, run with bounded concurrency. One settled slot per\n\t * input target: `fulfilled` with `undefined` means the issue does not exist or is not visible to this token;\n\t * `rejected` means that target could not be checked, never that it is absent.\n\t *\n\t * Failure isolation happens PER TARGET, mirroring `GitHostIntegration.getPullRequestsBatchResult`: the whole\n\t * call counts as a failure against the integration's request-exception budget only when EVERY slot rejected,\n\t * and not even then when a credential that checks out was refused by each target's own resource\n\t * (`settleBatchRefusals`), so a batch of mostly-good targets never spends more than one strike.\n\t *\n\t * Uncached on purpose: the caller owns caching, as with the git hosts' batch reads.\n\t */\n\tasync getIssuesByResourceIdBatchResult(\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<BatchSlot<IssueShape | undefined>[] | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\tconst getProviderIssue = this.getProviderIssueByResourceId;\n\t\tif (getProviderIssue == null) return { value: undefined };\n\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tconst slots = await mapSettledBounded(targets, providerFanOutConcurrency, t =>\n\t\t\t\tgetProviderIssue.call(this, session, t.resourceId, t.identifier, t.resourceUrl),\n\t\t\t);\n\n\t\t\tconst settled = await this.settleBatchRefusals(session, targets, slots);\n\n\t\t\tthis.resetRequestExceptionCount('getIssue');\n\t\t\treturn { value: settled, duration: performance.now() - start };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssue', ex, { scope: scope, connectionId: connectionId });\n\t\t\treturn { error: toError(ex), duration: performance.now() - start };\n\t\t}\n\t}\n\n\tprotected getProviderIssueByResourceId?(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_resourceId: string,\n\t\t_id: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined>;\n\n\t@trace()\n\tasync getAccountForResource(resource: T, connectionId?: string): Promise<Account | undefined> {\n\t\treturn (await this.getAccountForResourceResult(resource, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getAccountForResource}. Recovers a thrown error into `{ error }` so\n\t * callers can preserve its classification (e.g. a 401/403 → an `auth` warning that drives re-auth)\n\t * instead of collapsing every failure into an untyped `undefined`. Gated here (not on the wrapper) so\n\t * direct callers such as the ProviderBackend facade share the same dedup as `getAccountForResource`.\n\t */\n\t@gate()\n\tasync getAccountForResourceResult(\n\t\tresource: T,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<Account | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst account = await this.getProviderAccountForResource(session, resource);\n\t\t\tthis.resetRequestExceptionCount('getAccountForResource');\n\t\t\treturn { value: account };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getAccountForResource', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: T,\n\t): Promise<Account | undefined>;\n\n\t@trace()\n\tasync getResourcesForUser(connectionId?: string): Promise<T[] | undefined> {\n\t\treturn (await this.getResourcesForUserResult(connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getResourcesForUser}. Recovers thrown errors into `{ error }` so callers\n\t * can surface them as warnings rather than silently swallowing them to `undefined`. Gated here (not on the\n\t * wrapper) so direct callers such as the ProviderBackend facade share the same dedup as `getResourcesForUser`.\n\t */\n\t@gate()\n\tasync getResourcesForUserResult(connectionId?: string): Promise<IntegrationResult<T[] | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst resources = await this.getProviderResourcesForUser(session);\n\t\t\tthis.resetRequestExceptionCount('getResourcesForUser');\n\t\t\treturn { value: resources };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getResourcesForUser', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderResourcesForUser(session: ProviderAuthenticationSession): Promise<T[] | undefined>;\n\n\t/**\n\t * Project discovery for a set of resources, returning the SDK collection `{ values, metadata }` (completeness\n\t * + per-resource failures) so callers can warn on failed resources and set `fetchFailed` without discarding\n\t * the resources that succeeded. Thrown errors are recovered into `{ error }` for the same reason: a caller\n\t * surfaces them as a warning rather than swallowing them to `undefined`.\n\t */\n\t@trace()\n\tasync getProjectsForResourcesWithMetadataResult(\n\t\tresources: T[],\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProviderApiCollectionResult<T> | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst projects = await this.getProviderProjectsForResourcesWithMetadata(session, resources);\n\t\t\tawait this.confirmScopedAuthFailures(session, projects.metadata);\n\t\t\tthis.resetRequestExceptionCount('getProjectsForResources');\n\t\t\treturn { value: projects };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getProjectsForResources', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Metadata-aware provider project discovery. The default wraps the array-returning\n\t * {@link getProviderProjectsForResources} in `{ values }` with no metadata, so providers without a\n\t * fan-out completeness signal (Linear, Trello) need no change; Jira overrides it to preserve the SDK's\n\t * per-resource completeness/failures.\n\t */\n\tprotected async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<ProviderApiCollectionResult<T>> {\n\t\tconst projects = await this.getProviderProjectsForResources(session, resources);\n\t\treturn { values: projects ?? [] };\n\t}\n\n\tprotected abstract getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources: T[],\n\t): Promise<T[] | undefined>;\n\n\t@trace()\n\tasync getIssuesForProject(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getIssuesForProjectResult(project, options, connectionId))?.value;\n\t}\n\n\t/**\n\t * Result-returning core of {@link getIssuesForProject}. Recovers thrown errors into `{ error }` so callers\n\t * (e.g. the ProviderBackend facade) can surface a per-provider warning instead of a silent empty read —\n\t * important for providers that throw on unsupported operations (e.g. Linear's not-implemented issue read).\n\t */\n\tasync getIssuesForProjectResult(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<IssueShape[] | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst issues = await this.getProviderIssuesForProject(session, project, options);\n\t\t\tthis.resetRequestExceptionCount('getIssuesForProject');\n\t\t\treturn { value: issues };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/**\n\t * Truncation-aware variant of {@link getIssuesForProjectResult}. A provider that drains a project's issues\n\t * with an internal page backstop (Jira/Linear) overrides {@link getProviderIssuesForProjectWithTruncation}\n\t * to report when that backstop was hit, so the facade can surface an incomplete project read instead of\n\t * publishing it as complete. The default reports `truncated: false`.\n\t */\n\tasync getIssuesForProjectWithTruncationResult(\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t\tconnectionId?: string,\n\t): Promise<IntegrationResult<ProjectIssuesDrain | undefined>> {\n\t\tconst scope = getScopedLogger();\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null) return undefined;\n\n\t\ttry {\n\t\t\tconst result = await this.getProviderIssuesForProjectWithTruncation(session, project, options);\n\t\t\tawait this.confirmScopedAuthFailures(session, result?.metadata);\n\t\t\tthis.resetRequestExceptionCount('getIssuesForProject');\n\t\t\treturn { value: result };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getIssuesForProject', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\tprotected abstract getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined>;\n\n\t/**\n\t * Truncation-aware core of {@link getProviderIssuesForProject}. The default wraps the plain read and\n\t * reports `truncated: false`; a provider whose per-project drain is capped by a page backstop overrides\n\t * this to report incompleteness. Optional metadata lets providers surface structured per-project failures\n\t * (e.g. a page-level auth rejection) without discarding the already-fetched prefix.\n\t */\n\tprotected async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: T,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\tconst values = await this.getProviderIssuesForProject(session, project, options);\n\t\tif (values == null) return undefined;\n\t\treturn { values: values, truncated: false };\n\t}\n}\n"],"mappings":"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"}
@@ -7,9 +7,9 @@ import { UnidentifiedAuthor } from "../../../../git/models/author.js";
7
7
  import { DefaultBranch } from "../../../../git/models/defaultBranch.js";
8
8
  import { RepositoryMetadata } from "../../../../git/models/repositoryMetadata.js";
9
9
  import { TokenWithInfo } from "../../authentication/models.js";
10
+ import { AzureProjectDescriptor, AzureWorkItemStateCategory } from "./models.js";
10
11
  import { ProviderIssueSearchPage } from "../../models/issueReads.js";
11
12
  import { ProviderApiConfig } from "../apiConfig.js";
12
- import { AzureProjectDescriptor, AzureWorkItemStateCategory } from "./models.js";
13
13
  import { IntegrationServiceContext } from "../../context.js";
14
14
  //#region ../plus/integrations/src/providers/azure/azure.d.ts
15
15
  export declare class AzureDevOpsApi implements Disposable {
@@ -13,17 +13,13 @@ import { ProviderFetchError, isRateLimitResponse, toRateLimitError } from "../..
13
13
  import { azureWorkItemSearchResultLimit, parseAzureWorkItemSearchCursor, toAzureSearchPageSize, toAzureWorkItemSearchCursorKey, toAzureWorkItemSearchWiql } from "./search.js";
14
14
  import { decodePathSegment } from "../../utils/domain.utils.js";
15
15
  import { baseProviderApiConfig } from "../apiConfig.js";
16
- import { azurePullRequestStatusToState, azureWorkItemsStateCategoryToState, fromAzurePullRequest, fromAzureWorkItem, getAzurePullRequestWebUrl, isClosedAzurePullRequestStatus, isClosedAzureWorkItemStateCategory, normalizeAzureBranchName, sanitizeAzureRepositoryUrl } from "./models.js";
16
+ import { azurePullRequestStatusToState, azureWorkItemsStateCategoryToState, encodeAzurePathSegment, fromAzurePullRequest, fromAzureWorkItem, getAzurePullRequestWebUrl, isClosedAzurePullRequestStatus, isClosedAzureWorkItemStateCategory, normalizeAzureBranchName, sanitizeAzureRepositoryUrl } from "./models.js";
17
17
  import * as l10n from "@vscode/l10n";
18
18
  //#region ../plus/integrations/src/providers/azure/azure.ts
19
19
  const forkRepositoryUrlCacheTtl = 3e5;
20
20
  /** How long a work-item search's id snapshot outlives the last page read from it, and the most it lives at all. */
21
21
  const workItemSearchSnapshotTtl = 3e5;
22
22
  const workItemSearchSnapshotMaxTtl = 18e5;
23
- function encodePathSegment(value) {
24
- if (value === "." || value === "..") throw new Error(`Invalid Azure path segment '${value}'.`);
25
- return encodeURIComponent(value);
26
- }
27
23
  /** Whether Azure refused a WIQL query for matching more work items than it will return (`VS402337`). */
28
24
  function isWorkItemLimitRefusal(ex) {
29
25
  return ex instanceof RequestClientError && ex.message.includes("VS402337");
@@ -105,7 +101,7 @@ var AzureDevOpsApi = class {
105
101
  const scope = getScopedLogger();
106
102
  const { projectName, repoName } = parseAzureRepositoryDescriptor(repo);
107
103
  try {
108
- const pr = ((await this.request(provider, token, options?.baseUrl, `${encodePathSegment(owner)}/${encodePathSegment(projectName)}/_apis/git/repositories/${encodePathSegment(repoName)}/pullRequests?searchCriteria.status=all&searchCriteria.sourceRefName=${encodeURIComponent(`refs/heads/${branch}`)}`, { method: "GET" }, scope))?.value.sort((a, b) => {
104
+ const pr = ((await this.request(provider, token, options?.baseUrl, `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/pullRequests?searchCriteria.status=all&searchCriteria.sourceRefName=${encodeURIComponent(`refs/heads/${branch}`)}`, { method: "GET" }, scope))?.value.sort((a, b) => {
109
105
  const aIsOpen = a.status === "active" || a.status === "notSet";
110
106
  if (aIsOpen !== (b.status === "active" || b.status === "notSet")) return aIsOpen ? -1 : 1;
111
107
  const aDate = new Date(a.closedDate || a.creationDate);
@@ -123,7 +119,7 @@ var AzureDevOpsApi = class {
123
119
  const scope = getScopedLogger();
124
120
  const { projectName, repoName } = parseAzureRepositoryDescriptor(repo);
125
121
  try {
126
- const pr = (await this.request(provider, token, baseUrl, `${encodePathSegment(owner)}/${encodePathSegment(projectName)}/_apis/git/repositories/${encodePathSegment(repoName)}/pullrequestquery?api-version=4.1`, {
122
+ const pr = (await this.request(provider, token, baseUrl, `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/pullrequestquery?api-version=4.1`, {
127
123
  method: "POST",
128
124
  body: JSON.stringify({ queries: [{
129
125
  items: [rev],
@@ -131,7 +127,7 @@ var AzureDevOpsApi = class {
131
127
  }] })
132
128
  }, scope, cancellation))?.results[0]?.[rev]?.[0];
133
129
  if (pr == null) return void 0;
134
- const pullRequest = await this.request(provider, token, baseUrl, `${encodePathSegment(owner)}/${encodePathSegment(pr.repository.project.id)}/_apis/git/repositories/${encodePathSegment(pr.repository.id)}/pullRequests/${encodePathSegment(pr.pullRequestId.toString())}`, { method: "GET" }, scope, cancellation);
130
+ const pullRequest = await this.request(provider, token, baseUrl, `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(pr.repository.project.id)}/_apis/git/repositories/${encodeAzurePathSegment(pr.repository.id)}/pullRequests/${encodeAzurePathSegment(pr.pullRequestId.toString())}`, { method: "GET" }, scope, cancellation);
135
131
  if (pullRequest == null) return void 0;
136
132
  return await this.toPullRequest(pullRequest, provider, token, owner, baseUrl, scope, cancellation);
137
133
  } catch (ex) {
@@ -144,7 +140,7 @@ var AzureDevOpsApi = class {
144
140
  const scope = getScopedLogger();
145
141
  const { projectName, repoName } = parseAzureRepositoryDescriptor(repo);
146
142
  if (options?.type === void 0 || options?.type === "issue") try {
147
- const issueResult = await this.request(provider, token, options?.baseUrl, `${encodePathSegment(owner)}/${encodePathSegment(projectName)}/_apis/wit/workItems/${encodePathSegment(id)}`, { method: "GET" }, scope);
143
+ const issueResult = await this.request(provider, token, options?.baseUrl, `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/wit/workItems/${encodeAzurePathSegment(id)}`, { method: "GET" }, scope);
148
144
  if (issueResult != null) {
149
145
  const issueType = issueResult.fields["System.WorkItemType"];
150
146
  const state = issueResult.fields["System.State"];
@@ -170,7 +166,7 @@ var AzureDevOpsApi = class {
170
166
  }
171
167
  }
172
168
  if (options?.type === void 0 || options?.type === "pullrequest") try {
173
- const prResult = await this.request(provider, token, options?.baseUrl, `${encodePathSegment(owner)}/${encodePathSegment(projectName)}/_apis/git/repositories/${encodePathSegment(repoName)}/pullRequests/${encodePathSegment(id)}`, { method: "GET" }, scope);
169
+ const prResult = await this.request(provider, token, options?.baseUrl, `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/pullRequests/${encodeAzurePathSegment(id)}`, { method: "GET" }, scope);
174
170
  if (prResult != null) return {
175
171
  id: prResult.pullRequestId.toString(),
176
172
  type: "pullrequest",
@@ -195,7 +191,7 @@ var AzureDevOpsApi = class {
195
191
  async getIssue(provider, token, project, id, options) {
196
192
  const scope = getScopedLogger();
197
193
  try {
198
- const issueResult = await this.request(provider, token, options?.baseUrl, `${encodePathSegment(project.resourceName)}/${encodePathSegment(project.name)}/_apis/wit/workItems/${encodePathSegment(id)}`, { method: "GET" }, scope);
194
+ const issueResult = await this.request(provider, token, options?.baseUrl, `${encodeAzurePathSegment(project.resourceName)}/${encodeAzurePathSegment(project.name)}/_apis/wit/workItems/${encodeAzurePathSegment(id)}`, { method: "GET" }, scope);
199
195
  if (issueResult != null) {
200
196
  const issueType = issueResult.fields["System.WorkItemType"];
201
197
  const state = issueResult.fields["System.State"];
@@ -214,7 +210,7 @@ var AzureDevOpsApi = class {
214
210
  const scope = getScopedLogger();
215
211
  const { projectName, repoName } = parseAzureRepositoryDescriptor(repo);
216
212
  try {
217
- const author = (await this.request(provider, token, baseUrl, `${encodePathSegment(owner)}/${encodePathSegment(projectName)}/_apis/git/repositories/${encodePathSegment(repoName)}/commits/${encodePathSegment(rev)}`, { method: "GET" }, scope))?.author;
213
+ const author = (await this.request(provider, token, baseUrl, `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}/commits/${encodeAzurePathSegment(rev)}`, { method: "GET" }, scope))?.author;
218
214
  if (!author) return;
219
215
  return {
220
216
  provider,
@@ -287,7 +283,7 @@ var AzureDevOpsApi = class {
287
283
  async retrieveWorkItemTypeStates(workItemType, provider, token, owner, projectName, options) {
288
284
  const scope = getScopedLogger();
289
285
  try {
290
- return (await this.request(provider, token, options?.baseUrl, `${encodePathSegment(owner)}/${encodePathSegment(projectName)}/_apis/wit/workItemTypes/${encodePathSegment(workItemType)}/states`, { method: "GET" }, scope))?.value ?? [];
286
+ return (await this.request(provider, token, options?.baseUrl, `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(workItemType)}/states`, { method: "GET" }, scope))?.value ?? [];
291
287
  } catch (ex) {
292
288
  if (ex instanceof AuthenticationError) throw ex;
293
289
  scope?.error(ex);
@@ -376,7 +372,7 @@ var AzureDevOpsApi = class {
376
372
  const slice = ids.slice(offset, offset + pageSize);
377
373
  const values = [];
378
374
  if (slice.length > 0) {
379
- const workItems = await this.request(provider, token, options.baseUrl, `${encodePathSegment(collection)}/_apis/wit/workitemsbatch?api-version=5.0`, {
375
+ const workItems = await this.request(provider, token, options.baseUrl, `${encodeAzurePathSegment(collection)}/_apis/wit/workitemsbatch?api-version=5.0`, {
380
376
  method: "POST",
381
377
  body: JSON.stringify({
382
378
  ids: slice,
@@ -437,7 +433,7 @@ var AzureDevOpsApi = class {
437
433
  * answers; `timePrecision=true` is what lets the criteria's dates compare as instants — see `toWiqlDate`.
438
434
  */
439
435
  async queryWorkItemIds(provider, token, collection, baseUrl, wiql, top, scope, cancellation) {
440
- const result = await this.request(provider, token, baseUrl, `${encodePathSegment(collection)}/_apis/wit/wiql?$top=${top}&timePrecision=true&api-version=5.0`, {
436
+ const result = await this.request(provider, token, baseUrl, `${encodeAzurePathSegment(collection)}/_apis/wit/wiql?$top=${top}&timePrecision=true&api-version=5.0`, {
441
437
  method: "POST",
442
438
  body: JSON.stringify({ query: wiql })
443
439
  }, scope, cancellation);
@@ -446,7 +442,7 @@ var AzureDevOpsApi = class {
446
442
  }
447
443
  getRepository(provider, token, owner, repo, baseUrl, scope, cancellation) {
448
444
  const { projectName, repoName } = parseAzureRepositoryDescriptor(repo);
449
- return this.request(provider, token, baseUrl, `${encodePathSegment(owner)}/${encodePathSegment(projectName)}/_apis/git/repositories/${encodePathSegment(repoName)}?api-version=7.1`, { method: "GET" }, scope, cancellation);
445
+ return this.request(provider, token, baseUrl, `${encodeAzurePathSegment(owner)}/${encodeAzurePathSegment(projectName)}/_apis/git/repositories/${encodeAzurePathSegment(repoName)}?api-version=7.1`, { method: "GET" }, scope, cancellation);
450
446
  }
451
447
  async toPullRequest(pr, provider, token, owner, baseUrl, scope, cancellation) {
452
448
  const forkRepositoryUrls = await this.getForkRepositoryUrls(provider, token, owner, baseUrl, pr.forkSource?.repository, scope, cancellation);
@@ -479,7 +475,7 @@ var AzureDevOpsApi = class {
479
475
  }
480
476
  async fetchForkRepositoryUrls(provider, token, owner, baseUrl, repositoryId, cacheable, scope, cancellation) {
481
477
  try {
482
- const response = await this.request(provider, token, baseUrl, `${encodePathSegment(owner)}/_apis/git/repositories/${encodePathSegment(repositoryId)}?api-version=4.1`, { method: "GET" }, scope, cancellation);
478
+ const response = await this.request(provider, token, baseUrl, `${encodeAzurePathSegment(owner)}/_apis/git/repositories/${encodeAzurePathSegment(repositoryId)}?api-version=4.1`, { method: "GET" }, scope, cancellation);
483
479
  const webUrl = sanitizeAzureRepositoryUrl(response?.webUrl, baseUrl, owner);
484
480
  const cloneHttps = sanitizeAzureRepositoryUrl(response?.remoteUrl, baseUrl, owner);
485
481
  const url = webUrl ?? cloneHttps;