@gitkraken/core-gitlens 0.14.0 → 0.15.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 (62) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/dist/plus/integrations/collectionMetadata.d.ts +7 -0
  3. package/dist/plus/integrations/collectionMetadata.js +10 -1
  4. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  5. package/dist/plus/integrations/errors.d.ts +5 -0
  6. package/dist/plus/integrations/errors.js +10 -1
  7. package/dist/plus/integrations/errors.js.map +1 -1
  8. package/dist/plus/integrations/index.d.ts +2 -2
  9. package/dist/plus/integrations/index.js.map +1 -1
  10. package/dist/plus/integrations/models/gitHostIntegration.d.ts +6 -2
  11. package/dist/plus/integrations/models/gitHostIntegration.js +37 -7
  12. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  13. package/dist/plus/integrations/models/issueReads.d.ts +1 -1
  14. package/dist/plus/integrations/models/issuesIntegration.d.ts +33 -3
  15. package/dist/plus/integrations/models/issuesIntegration.js +76 -5
  16. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  17. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +9 -1
  18. package/dist/plus/integrations/providers/azureDevOpsServer.js +17 -9
  19. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  20. package/dist/plus/integrations/providers/bitbucket-server.js +3 -2
  21. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  22. package/dist/plus/integrations/providers/jira-server.d.ts +3 -0
  23. package/dist/plus/integrations/providers/jira-server.js +15 -7
  24. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  25. package/dist/plus/integrations/providers/jira.d.ts +8 -0
  26. package/dist/plus/integrations/providers/jira.js +88 -58
  27. package/dist/plus/integrations/providers/jira.js.map +1 -1
  28. package/dist/plus/integrations/providers/linear.d.ts +4 -0
  29. package/dist/plus/integrations/providers/linear.js +23 -2
  30. package/dist/plus/integrations/providers/linear.js.map +1 -1
  31. package/dist/plus/integrations/providers/providerErrors.d.ts +24 -0
  32. package/dist/plus/integrations/providers/providerErrors.js +41 -4
  33. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  34. package/dist/plus/integrations/providers/providersApi.d.ts +1 -1
  35. package/dist/plus/integrations/providers/providersApi.js +15 -4
  36. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  37. package/dist/plus/integrations/providers/trello.d.ts +1 -0
  38. package/dist/plus/integrations/providers/trello.js +3 -0
  39. package/dist/plus/integrations/providers/trello.js.map +1 -1
  40. package/dist/plus/integrations/reads/cursors.js +23 -5
  41. package/dist/plus/integrations/reads/cursors.js.map +1 -1
  42. package/dist/plus/integrations/reads/drains.d.ts +16 -0
  43. package/dist/plus/integrations/reads/drains.js +19 -1
  44. package/dist/plus/integrations/reads/drains.js.map +1 -1
  45. package/dist/plus/integrations/reads/hierarchy.js +5 -3
  46. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  47. package/dist/plus/integrations/reads/hierarchy.utils.d.ts +3 -2
  48. package/dist/plus/integrations/reads/hierarchy.utils.js +4 -7
  49. package/dist/plus/integrations/reads/hierarchy.utils.js.map +1 -1
  50. package/dist/plus/integrations/reads/issueTracker.js +71 -7
  51. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  52. package/dist/plus/integrations/reads/selectionConfirmation.d.ts +69 -0
  53. package/dist/plus/integrations/reads/selectionConfirmation.js +79 -0
  54. package/dist/plus/integrations/reads/selectionConfirmation.js.map +1 -0
  55. package/dist/plus/integrations/reads/warnings.d.ts +9 -3
  56. package/dist/plus/integrations/reads/warnings.js +19 -3
  57. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  58. package/dist/plus/integrations/results.d.ts +30 -3
  59. package/dist/plus/integrations/results.js +2 -1
  60. package/dist/plus/integrations/results.js.map +1 -1
  61. package/docs/integrations.md +59 -6
  62. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,25 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.15.0] - 2026-10-19
10
+
11
+ ### Added
12
+
13
+ - Adds `ProviderWarning.selector` (`ProviderWarningSelector`): `listIssueTrackerIssuesPage` now reports an `org` or `project` that matches nothing the connection discovered as one `kind: 'not-found'` warning with `selector: { kind: 'org' | 'project', value }`, on an empty page that is not `fetchFailed` and has no cursor, where it returned an empty page with no warning. Only a complete discovery reports it; when listing the resources or projects failed or was truncated, the read reports that failure as before. A miss is checked against the connection's lists as the provider holds them now, read directly and sharing nothing with the cache, and reported only from them, with `forceSync` too, once an account lookup on a resource they name confirms the connection's credential; when the check matches, the cache was stale, so it is dropped and the read made once more, and a site or project created since is read rather than reported. A selected Jira project deleted while the cached project list still names it reads as empty, as before, and its refusal drops that list, so the next read reports the miss; on Jira Data Center the refusal counts only when it names the user it authenticated (`X-AUSERNAME`), and fails the read otherwise. A `not-found` without `selector` is still a 404, 410 or 422 ([#6014](https://github.com/gitkraken/vscode-gitlens/issues/6014)) (plus/integrations)
14
+
15
+ ### Changed
16
+
17
+ - Changes Jira Data Center project-scoped failures to name the project by its key in `scope.projectId`, as Jira Cloud does, where they named its numeric id; the project is still read by id ([#6014](https://github.com/gitkraken/vscode-gitlens/issues/6014)) (plus/integrations)
18
+ - Changes `listIssueTrackerIssuesPage`'s cursor to track projects by resource and id only, no longer also by key, so a project whose key is renamed between pages is neither read again nor dropped. A cursor from an earlier version still resumes its projects ([#6014](https://github.com/gitkraken/vscode-gitlens/issues/6014)) (plus/integrations)
19
+
20
+ ### Fixed
21
+
22
+ - Fixes a Jira Data Center token the server rejects being read as projects that no longer exist on a `listIssueTrackerIssuesPage` read of every assignee: Jira Data Center serves such a token as the anonymous user and refuses its project searches with the same `400` "does not exist for the field 'project'", which read each project as empty, an unfailed page, and dropped the cached project list. A refusal that names no authenticated user in `X-AUSERNAME` is now a failed read, with a retry cursor, and keeps the list ([#6014](https://github.com/gitkraken/vscode-gitlens/issues/6014)) (plus/integrations)
23
+ - Fixes a Jira Data Center token the server rejects being read as an account with no projects: Jira Data Center serves such a token as the anonymous user, answering its project list with `200 []`, so `listProjects` returned an empty catalog and `listIssueTrackerIssuesPage` reported a selected site or project as a `selector` miss. A response that names `anonymous` in `X-AUSERNAME` to a request sent with a credential is now an authentication failure (`kind: 'auth'`) on every Jira Data Center read, and on every other read through `@gitkraken/provider-apis`; a 401 keeps its own error and body, and a throttle or a server failure its own error. A Bitbucket Data Center refusal naming `anonymous` no longer counts as one from a credential that authenticated ([#6014](https://github.com/gitkraken/vscode-gitlens/issues/6014)) (plus/integrations)
24
+ - Fixes a Linear team list or Jira Data Center project list that comes back with no data being read as an account with no teams or projects, an empty success: `listProjects` now reports it as `fetchFailed` with a completeness warning, and `listIssueTrackerIssuesPage` as `truncated` with a retry cursor, so a selected team or project is never reported as missing on its account ([#6014](https://github.com/gitkraken/vscode-gitlens/issues/6014)) (plus/integrations)
25
+ - Fixes a Jira Cloud site, Linear organization or Trello board discovery that answered with no data and no error reading as an account with nothing to read: `listIssueTrackerIssuesPage` returned an empty page with no warning, not `truncated` and without a retry cursor, and `listOrgs` / `listProjects` returned an empty list as complete. The discovery is now reported as incomplete, with a "Result completeness could not be confirmed" warning: the issues page is `truncated` and carries a retry cursor, and `listOrgs` / `listProjects` mark the provider `fetchFailed`. A discovery answering `[]` still means no resources, and an unconnected tracker still reads as empty ([#6024](https://github.com/gitkraken/vscode-gitlens/issues/6024)) (plus/integrations)
26
+ - Fixes Azure DevOps Server `listPullRequestsPage` and `listIssuesPage` reporting an empty result as complete for a repository scope named as collection `{vdir}` and project `{collection}` (a `{vdir}/{collection}/_git/{repo}` remote) while the collections can't be read. They now report that scope in a warning and mark the page incomplete, as the batch and branch reads already fail it; the searches and their counts re-read the collections and refuse a scope they can't place ([#6021](https://github.com/gitkraken/vscode-gitlens/issues/6021)) (plus/integrations)
27
+
9
28
  ## [0.14.0] - 2026-10-18
10
29
 
11
30
  ### Added
@@ -750,7 +769,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
750
769
 
751
770
  - Initial release. Bundles `@gitlens/utils`, `@gitlens/git`, `@gitlens/git-cli`, `@gitlens/ai`, and `@gitlens/git-github` into a single core npm package with subpath exports.
752
771
 
753
- [unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.14.0...HEAD
772
+ [unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.0...HEAD
773
+ [0.15.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.14.0...gitkraken:releases/core/v0.15.0
754
774
  [0.14.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.13.0...gitkraken:releases/core/v0.14.0
755
775
  [0.13.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.12.0...gitkraken:releases/core/v0.13.0
756
776
  [0.12.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.11.1...gitkraken:releases/core/v0.12.0
@@ -71,6 +71,13 @@ export interface ProviderScopeFailure extends CollectionScopeFailure {
71
71
  * is the case this failure exists to tell apart, and that sentence says the opposite.
72
72
  */
73
73
  export declare function toCollectionScopeFailure(scope: CollectionScopeFailure['scope'], ex: unknown): ProviderScopeFailure;
74
+ export declare const unsettledMissMessage = "Nothing read for a repository that couldn't be settled";
75
+ /**
76
+ * The failure a read records for a scope that read nothing while its repository couldn't be settled (see
77
+ * `GitHostIntegration.resolveRepositoryCoordinates`): it may have addressed another repository, so its miss is not a
78
+ * proven absence the read may publish as complete.
79
+ */
80
+ export declare function toUnsettledScopeFailure(scope: CollectionScopeFailure['scope']): ProviderScopeFailure;
74
81
  export declare function toCollectionFailureError(failure: ProviderScopeFailure, tokenWithInfo: TokenWithInfo): Error;
75
82
  /**
76
83
  * Whether `metadata` reports authentication failures only for scopes below the provider, with none for the
@@ -93,6 +93,15 @@ function toCollectionScopeFailure(scope, ex) {
93
93
  ...kind === "provider" && isProviderUnreachableError(ex) ? { cause: { reason: "unreachable" } } : {}
94
94
  };
95
95
  }
96
+ const unsettledMissMessage = "Nothing read for a repository that couldn't be settled";
97
+ /**
98
+ * The failure a read records for a scope that read nothing while its repository couldn't be settled (see
99
+ * `GitHostIntegration.resolveRepositoryCoordinates`): it may have addressed another repository, so its miss is not a
100
+ * proven absence the read may publish as complete.
101
+ */
102
+ function toUnsettledScopeFailure(scope) {
103
+ return toCollectionScopeFailure(scope, /* @__PURE__ */ new Error(unsettledMissMessage));
104
+ }
96
105
  function collectionFailureKindToWarningKind(kind) {
97
106
  switch (kind) {
98
107
  case "authentication": return "auth";
@@ -396,6 +405,6 @@ function mergeAssessmentInto(warnings, providerId, domain, connectionId, metadat
396
405
  };
397
406
  }
398
407
  //#endregion
399
- export { assessCollectionMetadata, attributeScopedAuthFailures, failsEveryScope, hasOnlyScopedAuthFailures, isIncompleteCollection, markCredentialRefusals, mergeAssessmentInto, throwIfCallerContractError, toCollectionFailureError, toCollectionFailureKind, toCollectionScopeFailure };
408
+ export { assessCollectionMetadata, attributeScopedAuthFailures, failsEveryScope, hasOnlyScopedAuthFailures, isIncompleteCollection, markCredentialRefusals, mergeAssessmentInto, throwIfCallerContractError, toCollectionFailureError, toCollectionFailureKind, toCollectionScopeFailure, toUnsettledScopeFailure, unsettledMissMessage };
400
409
 
401
410
  //# sourceMappingURL=collectionMetadata.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"collectionMetadata.js","names":[],"sources":["../../../../plus/integrations/src/collectionMetadata.ts"],"sourcesContent":["import type {\n\tCollectionMetadata,\n\tCollectionOmission,\n\tCollectionScope,\n\tCollectionScopeFailure,\n} from '@gitkraken/provider-apis';\n// Matched by its `code` discriminator rather than by `instanceof`: the SDK ships one bundle per entry point, so\n// the class reached through the root is not the same object as the one reached through `/providers`.\nimport { isInvalidRequestError, isUnsupportedSortError } from '@gitkraken/provider-apis';\nimport {\n\tAuthenticationError,\n\tRequestClientError,\n\tRequestNotFoundError,\n\tRequestRateLimitError,\n} from '@gitlens/git/errors.js';\nimport type { TokenWithInfo } from './authentication/models.js';\nimport type { IntegrationIds } from './constants.js';\nimport type { ResponseHeaders } from './errors.js';\nimport { getResponseHeader, isProviderUnreachableError, isRateLimitResponse } from './errors.js';\nimport { getProviderResponseBodyMessage } from './providers/providerErrors.js';\nimport type {\n\tProviderWarning,\n\tProviderWarningCause,\n\tProviderWarningOmission,\n\tProviderWarningScope,\n\tProviderWarningUnreadableItems,\n} from './results.js';\nimport { appendDedupedWarning, providerWarningMessage } from './results.js';\n\n/**\n * Re-throws an error that is a fact about the CALL rather than about one scope of a fan-out.\n *\n * Every provider fan-out catches per-scope rejections and records them as {@link CollectionScopeFailure}s, which is\n * right for auth, rate limits and a missing project: those really did happen to one scope, and the siblings that\n * succeeded must survive. A caller-contract error is the opposite — it is identical for every scope and was decided\n * before any request went out — so degrading it would hand back N indistinguishable failures and an empty `partial`\n * page, describing an invalid call as an incomplete read.\n *\n * Today that means the SDK's `UnsupportedSortError` and `InvalidRequestError` — the two codes it lists in its own\n * `CALLER_CONTRACT_ERROR_CODES`, both documented as \"never degraded into a `CollectionScopeFailure` or an\n * omission\". The second covers arguments rejected before the first request goes out: a project key list longer\n * than one Jira search can carry (or an empty one), a non-integer `pageSize`, an unknown current-user filter, or a\n * Jira Server read with no `baseUrl` to address. Called from every fan-out rather than spelled out in each, so a\n * fan-out added later inherits the rule instead of silently omitting it — which is exactly how Azure's\n * account-wide drain came to degrade it while its three siblings did not.\n */\nexport function throwIfCallerContractError(ex: unknown): void {\n\tif (isUnsupportedSortError(ex) || isInvalidRequestError(ex)) throw ex;\n}\n\n/**\n * Whether a failed query that covered several scopes would fail each scope's own read the same way: a rejected or\n * rate-limited token, or a call the SDK refused before sending. Anything else (a query the provider rejects because\n * one scope is gone or no longer visible, a server error) may be one scope's problem, so the scopes are worth\n * reading on their own.\n */\nexport function failsEveryScope(ex: unknown): boolean {\n\tconst kind = toCollectionFailureKind(ex);\n\treturn (\n\t\tkind === 'authentication' || kind === 'rate-limit' || isUnsupportedSortError(ex) || isInvalidRequestError(ex)\n\t);\n}\n\n/**\n * Maps a caught GitLens request error to the SDK collection failure vocabulary used inside provider fan-outs.\n */\nexport function toCollectionFailureKind(ex: unknown): CollectionScopeFailure['kind'] {\n\tif (ex instanceof AuthenticationError) return 'authentication';\n\tif (ex instanceof RequestRateLimitError) return 'rate-limit';\n\tif (ex instanceof RequestNotFoundError) return 'not-found';\n\treturn 'provider';\n}\n\n/**\n * What an authentication refusal's response said, kept on the failure so the provider can name its cause once the\n * credential behind it is confirmed (see `IntegrationBase.confirmScopedAuthFailures`). Only these facts are kept,\n * never the response: its headers carry the provider's session cookies.\n */\nexport interface ProviderRefusal {\n\tstatus: number;\n\t/** The provider's own explanation: Azure DevOps' `X-TFS-ServiceError`, or the error body's message. */\n\tdetail?: string;\n\t/** The provider's error type, when the body names one (Azure DevOps `typeKey`). */\n\ttypeKey?: string;\n\t/**\n\t * The OAuth app the refused token belongs to, when the provider says (GitHub's `X-OAuth-Client-Id`). Not a\n\t * secret: it is the app's public client id, which the page where a user asks an organization to approve the app\n\t * is addressed by.\n\t */\n\toauthClientId?: string;\n}\n\n/** A scope failure as recorded here: the SDK's shape, plus what the refusal said and, once it is known, why. */\nexport interface ProviderScopeFailure extends CollectionScopeFailure {\n\trefusal?: ProviderRefusal;\n\tcause?: ProviderWarningCause;\n\t/** The provider pinned the refusal on the credential itself, so it is published for the connection, unscoped. */\n\tcredentialRefused?: true;\n\t/**\n\t * The failure is items a page left out because they could not be read, the rest of its scope having been read. It\n\t * is published as an `'unreadable-items'` warning naming them, see `ProviderWarning.unreadableItems`.\n\t */\n\tunreadableItems?: ProviderWarningUnreadableItems;\n}\n\n/**\n * Reads the refusal off an `AuthenticationError` that carries the provider's response: the SDK adapter's plain\n * `{ status, headers, body }` (`providersApi.ts`), Octokit's `{ status, headers, data }` (the GitHub client's REST\n * reads), or the `Response` of a `ProviderFetchError`, whose body was already read into its message.\n */\nfunction toProviderRefusal(ex: unknown): ProviderRefusal | undefined {\n\tif (!(ex instanceof AuthenticationError)) return undefined;\n\n\tconst response = (ex.original as { response?: unknown } | undefined)?.response as\n\t\t| { status?: unknown; headers?: ResponseHeaders; body?: unknown; data?: unknown }\n\t\t| undefined;\n\tif (typeof response?.status !== 'number') return undefined;\n\n\t// Only a structured body: a string one is a page (a proxy's, a sign-in form), not an explanation.\n\tconst raw = response.body ?? response.data;\n\tconst body = typeof raw === 'object' && raw != null ? raw : undefined;\n\tconst detail =\n\t\tdecodeServiceError(getResponseHeader(response.headers, 'x-tfs-serviceerror')) ??\n\t\tgetProviderResponseBodyMessage(body);\n\tconst typeKey = body != null && 'typeKey' in body ? body.typeKey : undefined;\n\tconst oauthClientId = getResponseHeader(response.headers, 'x-oauth-client-id');\n\n\treturn {\n\t\tstatus: response.status,\n\t\t...(detail ? { detail: detail } : {}),\n\t\t...(typeof typeKey === 'string' ? { typeKey: typeKey } : {}),\n\t\t...(typeof oauthClientId === 'string' && oauthClientId ? { oauthClientId: oauthClientId } : {}),\n\t};\n}\n\n/** Azure DevOps sends `X-TFS-ServiceError` percent-encoded. */\nfunction decodeServiceError(value: unknown): string | undefined {\n\tif (typeof value !== 'string' || !value) return undefined;\n\n\ttry {\n\t\treturn decodeURIComponent(value).trim() || undefined;\n\t} catch {\n\t\treturn value.trim() || undefined;\n\t}\n}\n\n/**\n * Builds a structured SDK scope failure from a caught GitLens request error.\n *\n * An authentication refusal is described in the provider's own words when it gives any, and otherwise by the\n * error the provider's response raised, sanitized like any warning's (a page becomes its status), rather than by\n * `AuthenticationError`'s generic \"credentials are either invalid or expired\": a scope refusing a sound credential\n * is the case this failure exists to tell apart, and that sentence says the opposite.\n */\nexport function toCollectionScopeFailure(scope: CollectionScopeFailure['scope'], ex: unknown): ProviderScopeFailure {\n\tconst kind = toCollectionFailureKind(ex);\n\tconst refusal = toProviderRefusal(ex);\n\tconst message =\n\t\trefusal != null ? (refusal.detail ?? providerWarningMessage((ex as AuthenticationError).original)) : undefined;\n\treturn {\n\t\tscope: scope,\n\t\tkind: kind,\n\t\t...(message ? { message: message } : ex instanceof Error && ex.message ? { message: ex.message } : {}),\n\t\t...(refusal != null ? { refusal: refusal } : {}),\n\t\t...(kind === 'provider' && isProviderUnreachableError(ex) ? { cause: { reason: 'unreachable' as const } } : {}),\n\t};\n}\n\nfunction collectionFailureKindToWarningKind(kind: CollectionScopeFailure['kind']): ProviderWarning['kind'] {\n\tswitch (kind) {\n\t\tcase 'authentication':\n\t\t\treturn 'auth';\n\t\tcase 'rate-limit':\n\t\t\treturn 'rate-limit';\n\t\tcase 'not-found':\n\t\t\treturn 'not-found';\n\t\tdefault:\n\t\t\treturn 'other';\n\t}\n}\n\nfunction getCollectionFailureStatus(message: string | undefined): number | undefined {\n\tif (message == null) return undefined;\n\n\tconst match = /\\((\\d{3})\\)/.exec(message) ?? /^(?:HTTP\\s+)?(\\d{3})\\b/i.exec(message.trim());\n\treturn match != null ? Number(match[1]) : undefined;\n}\n\nfunction toCollectionFailureWarningKind(failure: ProviderScopeFailure): ProviderWarning['kind'] {\n\t// First: the skipped items' own error text is embedded in the message and may well carry a status.\n\tif (failure.unreadableItems != null) return 'unreadable-items';\n\n\tconst mapped = collectionFailureKindToWarningKind(failure.kind);\n\tif (mapped !== 'other' || (failure.kind !== 'provider' && failure.kind !== 'unknown')) return mapped;\n\n\t// provider-apis currently groups several HTTP statuses under `provider` in partial fan-outs. The custom\n\t// fetch adapter retains the status in `message`, so recover the same neutral classification the thrown-error\n\t// path exposes instead of downgrading these structured failures to `other`.\n\tconst status = getCollectionFailureStatus(failure.message);\n\tswitch (status) {\n\t\tcase 401:\n\t\t\treturn 'auth';\n\t\tcase 403:\n\t\t\treturn isRateLimitResponse({ status: status, message: failure.message }) ? 'rate-limit' : 'auth';\n\t\tcase 404:\n\t\tcase 410:\n\t\tcase 422:\n\t\t\treturn 'not-found';\n\t\tcase 429:\n\t\t\treturn 'rate-limit';\n\t\tdefault:\n\t\t\treturn mapped;\n\t}\n}\n\nfunction toCollectionFailureCause(\n\tfailure: ProviderScopeFailure,\n\tkind: ProviderWarning['kind'],\n): ProviderWarningCause | undefined {\n\treturn (\n\t\tfailure.cause ??\n\t\t(kind === 'other' &&\n\t\tisProviderUnreachableError({ ...failure, status: getCollectionFailureStatus(failure.message) })\n\t\t\t? { reason: 'unreachable' }\n\t\t\t: undefined)\n\t);\n}\n\nexport function toCollectionFailureError(failure: ProviderScopeFailure, tokenWithInfo: TokenWithInfo): Error {\n\tconst kind = toCollectionFailureWarningKind(failure);\n\tconst error = new Error(failure.message ?? 'Provider request failed', {\n\t\tcause: toCollectionFailureCause(failure, kind),\n\t});\n\tconst { accessToken, ...tokenInfo } = tokenWithInfo;\n\tif (kind === 'auth') return new AuthenticationError(tokenInfo, error.message, error);\n\tif (kind === 'rate-limit') return new RequestRateLimitError(error, accessToken, undefined);\n\tif (kind === 'not-found') return new RequestNotFoundError(error);\n\n\tconst status = getCollectionFailureStatus(failure.message);\n\tif (kind === 'other' && status != null && status >= 400 && status < 500) {\n\t\treturn new RequestClientError(error);\n\t}\n\n\treturn error;\n}\n\n/** ` (resource r, project p, repository o/n)` for the scope IDs present; empty when the scope names none. */\nfunction collectionScopeText(scope: CollectionScope | undefined): string {\n\tconst parts: string[] = [];\n\tif (scope?.resourceId != null) {\n\t\tparts.push(`resource ${scope.resourceId}`);\n\t}\n\tif (scope?.projectId != null) {\n\t\tparts.push(`project ${scope.projectId}`);\n\t}\n\tif (scope?.repositoryId != null) {\n\t\tparts.push(`repository ${scope.repositoryId}`);\n\t}\n\n\treturn parts.length ? ` (${parts.join(', ')})` : '';\n}\n\n/** The cause in the consumer's terms, for the prose that goes with the structured {@link ProviderWarningCause}. */\nfunction causeText(cause: ProviderWarningCause): string {\n\tswitch (cause.reason) {\n\t\tcase 'oauth-app-not-allowed':\n\t\t\treturn 'the organization does not allow third-party OAuth apps';\n\t\tcase 'access-denied':\n\t\t\treturn 'the account has no access to it';\n\t\tcase 'conditional-access':\n\t\t\treturn 'a Conditional Access policy blocked the request';\n\t\tcase 'unreachable':\n\t\t\treturn 'the provider server could not be reached';\n\t\tdefault:\n\t\t\tcause.reason satisfies never;\n\t\t\treturn 'the provider refused the request';\n\t}\n}\n\nfunction collectionFailureMessage(failure: ProviderScopeFailure): string {\n\tconst text = failure.cause != null ? causeText(failure.cause) : failure.message;\n\tconst detail = text != null ? `: ${text}` : '';\n\treturn `Failed to read ${failure.kind} scope${collectionScopeText(failure.scope)}${detail}`;\n}\n\n/**\n * Forwards the part of a failure's scope below the provider, so a consumer can tell \"one organization refused\n * this token\" from \"the connection's token is dead\" without parsing {@link collectionFailureMessage}.\n *\n * `providerId` is dropped: the warning already names its provider, and a scope naming nothing below it is a\n * failure of the connection itself. That is the distinction `getOrganizationsForUserResult` also draws before\n * counting an auth failure against the connection, though it reads a scope with no keys but `providerId`, so an\n * absent scope does not count there. Forwarding such a scope would make an account-wide failure look confined.\n *\n * Copied field by field, for the reason {@link toProviderWarningOmission} copies its scope.\n */\nfunction toProviderWarningScope(scope: CollectionScope | undefined): ProviderWarningScope | undefined {\n\tif (scope == null) return undefined;\n\n\tconst { resourceId, projectId, repositoryId } = scope;\n\tif (resourceId == null && projectId == null && repositoryId == null) return undefined;\n\n\treturn {\n\t\t...(resourceId != null ? { resourceId: resourceId } : {}),\n\t\t...(projectId != null ? { projectId: projectId } : {}),\n\t\t...(repositoryId != null ? { repositoryId: repositoryId } : {}),\n\t};\n}\n\n/**\n * Whether `metadata` reports authentication failures only for scopes below the provider, with none for the\n * connection itself. A credential revoked since a provider cached its discovery produces exactly this shape,\n * the same one a scope's own refusal does; `IntegrationBase.confirmScopedAuthFailures` tells them apart.\n *\n * Classified exactly as {@link assessCollectionMetadata} classifies the warnings, so it agrees with what a\n * consumer would be shown.\n */\nexport function hasOnlyScopedAuthFailures(metadata: CollectionMetadata | undefined): boolean {\n\tlet scoped = false;\n\tfor (const failure of metadata?.failures ?? []) {\n\t\tif (toCollectionFailureWarningKind(failure) !== 'auth') continue;\n\t\tif (toProviderWarningScope(failure.scope) == null || (failure as ProviderScopeFailure).credentialRefused) {\n\t\t\treturn false;\n\t\t}\n\n\t\tscoped = true;\n\t}\n\treturn scoped;\n}\n\n/** The scoped authentication failures in `metadata` that kept their refusal, with the scope they are forwarded as. */\nfunction* refusedScopes(\n\tmetadata: CollectionMetadata | undefined,\n): Generator<{ failure: ProviderScopeFailure & { refusal: ProviderRefusal }; scope: ProviderWarningScope }> {\n\tfor (const failure of (metadata?.failures ?? []) as ProviderScopeFailure[]) {\n\t\tif (\n\t\t\tfailure.refusal == null ||\n\t\t\tfailure.credentialRefused ||\n\t\t\ttoCollectionFailureWarningKind(failure) !== 'auth'\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst scope = toProviderWarningScope(failure.scope);\n\t\tif (scope == null) continue;\n\n\t\tyield { failure: failure as ProviderScopeFailure & { refusal: ProviderRefusal }, scope: scope };\n\t}\n}\n\n/**\n * Marks each scoped authentication failure in `metadata` whose refusal the provider pins on the credential, so it is\n * published for the connection rather than for its scope. Returns whether any was.\n */\nexport function markCredentialRefusals(\n\tmetadata: CollectionMetadata | undefined,\n\tisCredentialRefusal: (refusal: ProviderRefusal) => boolean,\n): boolean {\n\tlet marked = false;\n\tfor (const { failure } of refusedScopes(metadata)) {\n\t\tif (!isCredentialRefusal(failure.refusal)) continue;\n\n\t\tfailure.credentialRefused = true;\n\t\tmarked = true;\n\t}\n\treturn marked;\n}\n\n/**\n * Names the cause of each scoped authentication failure in `metadata` that kept its refusal. Only for a read whose\n * credential was just confirmed: before that, the refusals this can name look exactly like a dead credential.\n */\nexport function attributeScopedAuthFailures(\n\tmetadata: CollectionMetadata | undefined,\n\tdescribe: (refusal: ProviderRefusal, scope: ProviderWarningScope) => ProviderWarningCause | undefined,\n): void {\n\tfor (const { failure, scope } of refusedScopes(metadata)) {\n\t\tif (failure.cause != null) continue;\n\n\t\tconst cause = describe(failure.refusal, scope);\n\t\tif (cause != null) {\n\t\t\tfailure.cause = cause;\n\t\t}\n\t}\n}\n\n/**\n * Explains one omission in the consumer's terms — what was left out and, where the SDK reports it, how much.\n *\n * An omission is a completeness fact, never a failure: the read succeeded, and the provider (or the SDK's own\n * recovery budget) is what withheld results. That is why these never contribute to `fetchFailed`. Whether a\n * retry would recover anything is a separate question this layer cannot answer — see the `recovery` note on\n * {@link toProviderWarningOmission}.\n */\nfunction collectionOmissionMessage(omission: CollectionOmission): string {\n\tconst scopeText = collectionScopeText(omission.scope);\n\tswitch (omission.kind) {\n\t\tcase 'provider-limit':\n\t\t\t// `totalCount` is the only figure saying how much was withheld; both fields are optional, so degrade\n\t\t\t// through the shapes the SDK can actually emit rather than printing `undefined`.\n\t\t\tif (omission.totalCount != null && omission.limit != null) {\n\t\t\t\treturn `Search${scopeText} matched ${omission.totalCount} results, but the provider exposes at most ${omission.limit}`;\n\t\t\t}\n\t\t\tif (omission.limit != null) {\n\t\t\t\treturn `Search${scopeText} exceeded the provider limit of ${omission.limit} results`;\n\t\t\t}\n\t\t\treturn `Search${scopeText} exceeded the provider's result limit`;\n\t\tcase 'recovery-budget':\n\t\t\treturn `Stopped recovering omitted results${scopeText} after reaching the request budget${\n\t\t\t\tomission.limit != null ? ` of ${omission.limit} requests` : ''\n\t\t\t}`;\n\t\tcase 'pagination-incomplete':\n\t\t\treturn `More results are available${scopeText} than this read returned`;\n\t\tdefault:\n\t\t\t// `CollectionOmissionKind` is a closed union, so this is unreachable today. Kept as a compile-time\n\t\t\t// guard rather than a silent fallback: an SDK bump that adds a kind breaks the build here instead\n\t\t\t// of quietly degrading it to a vague message.\n\t\t\tomission.kind satisfies never;\n\t\t\treturn `Some results were omitted${scopeText}; the read is incomplete`;\n\t}\n}\n\n/**\n * Forwards an SDK omission as the structured signal consumers read instead of parsing {@link\n * collectionOmissionMessage}'s prose.\n *\n * `results.ts` re-spells `CollectionOmissionKind` rather than importing it, so the published warning surface\n * carries no `@gitkraken/provider-apis` types (see the export block in `index.ts`). The return type is what\n * keeps the two unions honest: an SDK bump that adds a member fails to compile here, alongside\n * `collectionOmissionMessage`'s `satisfies never`.\n *\n * `scope` is copied because the SDK's own object is retained and re-merged across drained pages\n * (`providerPaging.ts`), and this one crosses the package boundary.\n *\n * `recovery` is always `'none'`, deliberately, and a scoped `pagination-incomplete` is why it looks wrong: the\n * SDK emits that one shape from situations with opposite remedies and says so itself — \"Either the read\n * deliberately took one page per scope, or the provider advertised another page it gave no way to reach.\" Its\n * `collectAcrossScopes` producers (Azure, Bitbucket, Bitbucket Server) are the first, its `drainAcrossScopes`\n * producers (GitLab, Jira) the second, and those report an omission only when a cursor STALLED — so re-reading\n * that scope stalls at the identical page. Nothing in `CollectionOmission` separates the two, so claiming\n * recoverability would ship the dead-end button `recovery` exists to prevent. Making the recoverable case\n * claimable needs provider-apis to say whether a scope was drained or merely sampled: a fact to forward, not\n * to guess here.\n */\nfunction toProviderWarningOmission(omission: CollectionOmission): ProviderWarningOmission {\n\treturn {\n\t\tkind: omission.kind,\n\t\trecovery: 'none',\n\t\t...(omission.limit != null ? { limit: omission.limit } : {}),\n\t\t// The SDK types this `number | null | undefined`; normalize to one absence for consumers.\n\t\t...(omission.totalCount != null ? { totalCount: omission.totalCount } : {}),\n\t\t...(omission.scope != null ? { scope: { ...omission.scope } } : {}),\n\t};\n}\n\n/**\n * Whether SDK metadata describes a read that may be missing results.\n *\n * A reported omission counts on its own rather than deferring to `completeness`. The SDK does couple the\n * two — every site that emits an omission degrades completeness in the same call — so today the omission\n * clause changes no outcome. It is defensive: the two facts arrive as independent fields, and a read that\n * names what it left out must not be publishable as whole if a future producer sets only one of them.\n *\n * This is the single definition of \"may be missing results\", shared with `getPagedResult`, so one metadata\n * object cannot be truncated at one layer and complete at another.\n */\nexport function isIncompleteCollection(metadata: CollectionMetadata | undefined): boolean {\n\tif (metadata == null) return false;\n\n\treturn metadata.completeness !== 'complete' || (metadata.omissions?.length ?? 0) > 0;\n}\n\n/**\n * Converts internal SDK collection metadata into neutral provider facade signals.\n *\n * `requestFailed` is `fetchFailed` without the failures that only report items a read left unread: those fail the read,\n * since the items are missing, but every request answered, so the omissions the read reported still hold.\n */\nexport function assessCollectionMetadata(\n\tproviderId: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tmetadata: CollectionMetadata | undefined,\n): { warnings: ProviderWarning[]; fetchFailed: boolean; requestFailed: boolean; truncated: boolean } {\n\tif (metadata == null) return { warnings: [], fetchFailed: false, requestFailed: false, truncated: false };\n\n\tconst warnings: ProviderWarning[] = [];\n\tconst failures: ProviderScopeFailure[] = metadata.failures ?? [];\n\tfor (const failure of failures) {\n\t\tconst kind = toCollectionFailureWarningKind(failure);\n\t\tconst cause = toCollectionFailureCause(failure, kind);\n\t\tconst scope = failure.credentialRefused ? undefined : toProviderWarningScope(failure.scope);\n\t\tappendDedupedWarning(warnings, {\n\t\t\tproviderId: providerId,\n\t\t\tdomain: domain,\n\t\t\tconnectionId: connectionId,\n\t\t\t// A credential-level refusal names no scope in prose either, so every scope's copy of it is one warning.\n\t\t\tmessage: collectionFailureMessage({\n\t\t\t\t...failure,\n\t\t\t\tcause: cause,\n\t\t\t\t...(failure.credentialRefused ? { scope: undefined } : {}),\n\t\t\t}),\n\t\t\tkind: kind,\n\t\t\tisAuth: kind === 'auth',\n\t\t\t...(scope != null ? { scope: scope } : {}),\n\t\t\t// Copied for the reason the scope is: the failure object is retained and re-merged across pages.\n\t\t\t...(cause != null ? { cause: { ...cause } } : {}),\n\t\t\t...(failure.unreadableItems != null\n\t\t\t\t? {\n\t\t\t\t\t\tunreadableItems: {\n\t\t\t\t\t\t\tkeys: [...failure.unreadableItems.keys],\n\t\t\t\t\t\t\tcomplete: failure.unreadableItems.complete,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t}\n\n\t// Omissions explain WHY a read is incomplete when nothing failed — a provider cap, an exhausted recovery\n\t// budget, an undrained scope. They classify as `other`, never `auth`, and are deliberately excluded from\n\t// `fetchFailed` below: the request itself succeeded. `omission` carries that same fact structurally, so a\n\t// consumer can act on it without parsing the message.\n\tconst omissions = metadata.omissions ?? [];\n\tfor (const omission of omissions) {\n\t\tappendDedupedWarning(warnings, {\n\t\t\tproviderId: providerId,\n\t\t\tdomain: domain,\n\t\t\tconnectionId: connectionId,\n\t\t\tmessage: collectionOmissionMessage(omission),\n\t\t\tkind: 'other',\n\t\t\tisAuth: false,\n\t\t\tomission: toProviderWarningOmission(omission),\n\t\t});\n\t}\n\n\tconst incomplete = isIncompleteCollection(metadata);\n\t// Only fall back to the generic message when nothing more specific was reported; an omission already\n\t// explains the incompleteness in the consumer's terms, so adding this on top would be noise.\n\t//\n\t// This one deliberately carries no `omission`: it fires precisely when the SDK reported incompleteness\n\t// WITHOUT saying what was left out, so there is no structured fact to forward and synthesizing one would\n\t// assert a specificity this layer does not have.\n\tif (incomplete && failures.length === 0 && omissions.length === 0) {\n\t\tappendDedupedWarning(warnings, {\n\t\t\tproviderId: providerId,\n\t\t\tdomain: domain,\n\t\t\tconnectionId: connectionId,\n\t\t\tmessage:\n\t\t\t\tmetadata.completeness === 'partial'\n\t\t\t\t\t? 'Some results were omitted; the read is incomplete'\n\t\t\t\t\t: 'Result completeness could not be confirmed',\n\t\t\tkind: 'other',\n\t\t\tisAuth: false,\n\t\t});\n\t}\n\n\treturn {\n\t\twarnings: warnings,\n\t\tfetchFailed: failures.length > 0,\n\t\trequestFailed: failures.some(failure => failure.unreadableItems == null),\n\t\ttruncated: incomplete,\n\t};\n}\n\n/**\n * Appends neutral warnings derived from SDK metadata and returns its failure/truncation assessment.\n *\n * `reported` says whether the metadata described this read's incompleteness at all. A caller that would\n * otherwise add its own generic \"this was truncated\" warning uses it to stay quiet — the same rule\n * {@link assessCollectionMetadata} applies to its own generic fallback, one layer up: whatever the metadata\n * reported already names this provider and, unless it was that same fallback, says something a consumer can\n * act on. Restating it in a weaker vocabulary is how one gap becomes two warnings with two different verdicts.\n *\n * `truncated` is the near-synonym that must NOT be used for that question: it comes from\n * {@link isIncompleteCollection}, which does not consider failures, so metadata carrying a typed failure\n * alongside `completeness: 'complete'` reports `truncated: false` while having already appended the warning\n * that explains everything. Gating on it would restate exactly the case this field exists to keep quiet.\n */\nexport function mergeAssessmentInto(\n\twarnings: ProviderWarning[],\n\tproviderId: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tmetadata: CollectionMetadata | undefined,\n): { fetchFailed: boolean; truncated: boolean; reported: boolean } {\n\tconst assessment = assessCollectionMetadata(providerId, domain, connectionId, metadata);\n\tfor (const warning of assessment.warnings) {\n\t\tappendDedupedWarning(warnings, warning);\n\t}\n\treturn {\n\t\tfetchFailed: assessment.fetchFailed,\n\t\ttruncated: assessment.truncated,\n\t\t// What the assessment PRODUCED, not what `appendDedupedWarning` accepted: a warning dropped as a\n\t\t// duplicate is still one the consumer will see, so it still makes a restatement redundant.\n\t\treported: assessment.warnings.length > 0,\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"collectionMetadata.js","names":[],"sources":["../../../../plus/integrations/src/collectionMetadata.ts"],"sourcesContent":["import type {\n\tCollectionMetadata,\n\tCollectionOmission,\n\tCollectionScope,\n\tCollectionScopeFailure,\n} from '@gitkraken/provider-apis';\n// Matched by its `code` discriminator rather than by `instanceof`: the SDK ships one bundle per entry point, so\n// the class reached through the root is not the same object as the one reached through `/providers`.\nimport { isInvalidRequestError, isUnsupportedSortError } from '@gitkraken/provider-apis';\nimport {\n\tAuthenticationError,\n\tRequestClientError,\n\tRequestNotFoundError,\n\tRequestRateLimitError,\n} from '@gitlens/git/errors.js';\nimport type { TokenWithInfo } from './authentication/models.js';\nimport type { IntegrationIds } from './constants.js';\nimport type { ResponseHeaders } from './errors.js';\nimport { getResponseHeader, isProviderUnreachableError, isRateLimitResponse } from './errors.js';\nimport { getProviderResponseBodyMessage } from './providers/providerErrors.js';\nimport type {\n\tProviderWarning,\n\tProviderWarningCause,\n\tProviderWarningOmission,\n\tProviderWarningScope,\n\tProviderWarningUnreadableItems,\n} from './results.js';\nimport { appendDedupedWarning, providerWarningMessage } from './results.js';\n\n/**\n * Re-throws an error that is a fact about the CALL rather than about one scope of a fan-out.\n *\n * Every provider fan-out catches per-scope rejections and records them as {@link CollectionScopeFailure}s, which is\n * right for auth, rate limits and a missing project: those really did happen to one scope, and the siblings that\n * succeeded must survive. A caller-contract error is the opposite — it is identical for every scope and was decided\n * before any request went out — so degrading it would hand back N indistinguishable failures and an empty `partial`\n * page, describing an invalid call as an incomplete read.\n *\n * Today that means the SDK's `UnsupportedSortError` and `InvalidRequestError` — the two codes it lists in its own\n * `CALLER_CONTRACT_ERROR_CODES`, both documented as \"never degraded into a `CollectionScopeFailure` or an\n * omission\". The second covers arguments rejected before the first request goes out: a project key list longer\n * than one Jira search can carry (or an empty one), a non-integer `pageSize`, an unknown current-user filter, or a\n * Jira Server read with no `baseUrl` to address. Called from every fan-out rather than spelled out in each, so a\n * fan-out added later inherits the rule instead of silently omitting it — which is exactly how Azure's\n * account-wide drain came to degrade it while its three siblings did not.\n */\nexport function throwIfCallerContractError(ex: unknown): void {\n\tif (isUnsupportedSortError(ex) || isInvalidRequestError(ex)) throw ex;\n}\n\n/**\n * Whether a failed query that covered several scopes would fail each scope's own read the same way: a rejected or\n * rate-limited token, or a call the SDK refused before sending. Anything else (a query the provider rejects because\n * one scope is gone or no longer visible, a server error) may be one scope's problem, so the scopes are worth\n * reading on their own.\n */\nexport function failsEveryScope(ex: unknown): boolean {\n\tconst kind = toCollectionFailureKind(ex);\n\treturn (\n\t\tkind === 'authentication' || kind === 'rate-limit' || isUnsupportedSortError(ex) || isInvalidRequestError(ex)\n\t);\n}\n\n/**\n * Maps a caught GitLens request error to the SDK collection failure vocabulary used inside provider fan-outs.\n */\nexport function toCollectionFailureKind(ex: unknown): CollectionScopeFailure['kind'] {\n\tif (ex instanceof AuthenticationError) return 'authentication';\n\tif (ex instanceof RequestRateLimitError) return 'rate-limit';\n\tif (ex instanceof RequestNotFoundError) return 'not-found';\n\treturn 'provider';\n}\n\n/**\n * What an authentication refusal's response said, kept on the failure so the provider can name its cause once the\n * credential behind it is confirmed (see `IntegrationBase.confirmScopedAuthFailures`). Only these facts are kept,\n * never the response: its headers carry the provider's session cookies.\n */\nexport interface ProviderRefusal {\n\tstatus: number;\n\t/** The provider's own explanation: Azure DevOps' `X-TFS-ServiceError`, or the error body's message. */\n\tdetail?: string;\n\t/** The provider's error type, when the body names one (Azure DevOps `typeKey`). */\n\ttypeKey?: string;\n\t/**\n\t * The OAuth app the refused token belongs to, when the provider says (GitHub's `X-OAuth-Client-Id`). Not a\n\t * secret: it is the app's public client id, which the page where a user asks an organization to approve the app\n\t * is addressed by.\n\t */\n\toauthClientId?: string;\n}\n\n/** A scope failure as recorded here: the SDK's shape, plus what the refusal said and, once it is known, why. */\nexport interface ProviderScopeFailure extends CollectionScopeFailure {\n\trefusal?: ProviderRefusal;\n\tcause?: ProviderWarningCause;\n\t/** The provider pinned the refusal on the credential itself, so it is published for the connection, unscoped. */\n\tcredentialRefused?: true;\n\t/**\n\t * The failure is items a page left out because they could not be read, the rest of its scope having been read. It\n\t * is published as an `'unreadable-items'` warning naming them, see `ProviderWarning.unreadableItems`.\n\t */\n\tunreadableItems?: ProviderWarningUnreadableItems;\n}\n\n/**\n * Reads the refusal off an `AuthenticationError` that carries the provider's response: the SDK adapter's plain\n * `{ status, headers, body }` (`providersApi.ts`), Octokit's `{ status, headers, data }` (the GitHub client's REST\n * reads), or the `Response` of a `ProviderFetchError`, whose body was already read into its message.\n */\nfunction toProviderRefusal(ex: unknown): ProviderRefusal | undefined {\n\tif (!(ex instanceof AuthenticationError)) return undefined;\n\n\tconst response = (ex.original as { response?: unknown } | undefined)?.response as\n\t\t| { status?: unknown; headers?: ResponseHeaders; body?: unknown; data?: unknown }\n\t\t| undefined;\n\tif (typeof response?.status !== 'number') return undefined;\n\n\t// Only a structured body: a string one is a page (a proxy's, a sign-in form), not an explanation.\n\tconst raw = response.body ?? response.data;\n\tconst body = typeof raw === 'object' && raw != null ? raw : undefined;\n\tconst detail =\n\t\tdecodeServiceError(getResponseHeader(response.headers, 'x-tfs-serviceerror')) ??\n\t\tgetProviderResponseBodyMessage(body);\n\tconst typeKey = body != null && 'typeKey' in body ? body.typeKey : undefined;\n\tconst oauthClientId = getResponseHeader(response.headers, 'x-oauth-client-id');\n\n\treturn {\n\t\tstatus: response.status,\n\t\t...(detail ? { detail: detail } : {}),\n\t\t...(typeof typeKey === 'string' ? { typeKey: typeKey } : {}),\n\t\t...(typeof oauthClientId === 'string' && oauthClientId ? { oauthClientId: oauthClientId } : {}),\n\t};\n}\n\n/** Azure DevOps sends `X-TFS-ServiceError` percent-encoded. */\nfunction decodeServiceError(value: unknown): string | undefined {\n\tif (typeof value !== 'string' || !value) return undefined;\n\n\ttry {\n\t\treturn decodeURIComponent(value).trim() || undefined;\n\t} catch {\n\t\treturn value.trim() || undefined;\n\t}\n}\n\n/**\n * Builds a structured SDK scope failure from a caught GitLens request error.\n *\n * An authentication refusal is described in the provider's own words when it gives any, and otherwise by the\n * error the provider's response raised, sanitized like any warning's (a page becomes its status), rather than by\n * `AuthenticationError`'s generic \"credentials are either invalid or expired\": a scope refusing a sound credential\n * is the case this failure exists to tell apart, and that sentence says the opposite.\n */\nexport function toCollectionScopeFailure(scope: CollectionScopeFailure['scope'], ex: unknown): ProviderScopeFailure {\n\tconst kind = toCollectionFailureKind(ex);\n\tconst refusal = toProviderRefusal(ex);\n\tconst message =\n\t\trefusal != null ? (refusal.detail ?? providerWarningMessage((ex as AuthenticationError).original)) : undefined;\n\treturn {\n\t\tscope: scope,\n\t\tkind: kind,\n\t\t...(message ? { message: message } : ex instanceof Error && ex.message ? { message: ex.message } : {}),\n\t\t...(refusal != null ? { refusal: refusal } : {}),\n\t\t...(kind === 'provider' && isProviderUnreachableError(ex) ? { cause: { reason: 'unreachable' as const } } : {}),\n\t};\n}\n\nexport const unsettledMissMessage = \"Nothing read for a repository that couldn't be settled\";\n\n/**\n * The failure a read records for a scope that read nothing while its repository couldn't be settled (see\n * `GitHostIntegration.resolveRepositoryCoordinates`): it may have addressed another repository, so its miss is not a\n * proven absence the read may publish as complete.\n */\nexport function toUnsettledScopeFailure(scope: CollectionScopeFailure['scope']): ProviderScopeFailure {\n\treturn toCollectionScopeFailure(scope, new Error(unsettledMissMessage));\n}\n\nfunction collectionFailureKindToWarningKind(kind: CollectionScopeFailure['kind']): ProviderWarning['kind'] {\n\tswitch (kind) {\n\t\tcase 'authentication':\n\t\t\treturn 'auth';\n\t\tcase 'rate-limit':\n\t\t\treturn 'rate-limit';\n\t\tcase 'not-found':\n\t\t\treturn 'not-found';\n\t\tdefault:\n\t\t\treturn 'other';\n\t}\n}\n\nfunction getCollectionFailureStatus(message: string | undefined): number | undefined {\n\tif (message == null) return undefined;\n\n\tconst match = /\\((\\d{3})\\)/.exec(message) ?? /^(?:HTTP\\s+)?(\\d{3})\\b/i.exec(message.trim());\n\treturn match != null ? Number(match[1]) : undefined;\n}\n\nfunction toCollectionFailureWarningKind(failure: ProviderScopeFailure): ProviderWarning['kind'] {\n\t// First: the skipped items' own error text is embedded in the message and may well carry a status.\n\tif (failure.unreadableItems != null) return 'unreadable-items';\n\n\tconst mapped = collectionFailureKindToWarningKind(failure.kind);\n\tif (mapped !== 'other' || (failure.kind !== 'provider' && failure.kind !== 'unknown')) return mapped;\n\n\t// provider-apis currently groups several HTTP statuses under `provider` in partial fan-outs. The custom\n\t// fetch adapter retains the status in `message`, so recover the same neutral classification the thrown-error\n\t// path exposes instead of downgrading these structured failures to `other`.\n\tconst status = getCollectionFailureStatus(failure.message);\n\tswitch (status) {\n\t\tcase 401:\n\t\t\treturn 'auth';\n\t\tcase 403:\n\t\t\treturn isRateLimitResponse({ status: status, message: failure.message }) ? 'rate-limit' : 'auth';\n\t\tcase 404:\n\t\tcase 410:\n\t\tcase 422:\n\t\t\treturn 'not-found';\n\t\tcase 429:\n\t\t\treturn 'rate-limit';\n\t\tdefault:\n\t\t\treturn mapped;\n\t}\n}\n\nfunction toCollectionFailureCause(\n\tfailure: ProviderScopeFailure,\n\tkind: ProviderWarning['kind'],\n): ProviderWarningCause | undefined {\n\treturn (\n\t\tfailure.cause ??\n\t\t(kind === 'other' &&\n\t\tisProviderUnreachableError({ ...failure, status: getCollectionFailureStatus(failure.message) })\n\t\t\t? { reason: 'unreachable' }\n\t\t\t: undefined)\n\t);\n}\n\nexport function toCollectionFailureError(failure: ProviderScopeFailure, tokenWithInfo: TokenWithInfo): Error {\n\tconst kind = toCollectionFailureWarningKind(failure);\n\tconst error = new Error(failure.message ?? 'Provider request failed', {\n\t\tcause: toCollectionFailureCause(failure, kind),\n\t});\n\tconst { accessToken, ...tokenInfo } = tokenWithInfo;\n\tif (kind === 'auth') return new AuthenticationError(tokenInfo, error.message, error);\n\tif (kind === 'rate-limit') return new RequestRateLimitError(error, accessToken, undefined);\n\tif (kind === 'not-found') return new RequestNotFoundError(error);\n\n\tconst status = getCollectionFailureStatus(failure.message);\n\tif (kind === 'other' && status != null && status >= 400 && status < 500) {\n\t\treturn new RequestClientError(error);\n\t}\n\n\treturn error;\n}\n\n/** ` (resource r, project p, repository o/n)` for the scope IDs present; empty when the scope names none. */\nfunction collectionScopeText(scope: CollectionScope | undefined): string {\n\tconst parts: string[] = [];\n\tif (scope?.resourceId != null) {\n\t\tparts.push(`resource ${scope.resourceId}`);\n\t}\n\tif (scope?.projectId != null) {\n\t\tparts.push(`project ${scope.projectId}`);\n\t}\n\tif (scope?.repositoryId != null) {\n\t\tparts.push(`repository ${scope.repositoryId}`);\n\t}\n\n\treturn parts.length ? ` (${parts.join(', ')})` : '';\n}\n\n/** The cause in the consumer's terms, for the prose that goes with the structured {@link ProviderWarningCause}. */\nfunction causeText(cause: ProviderWarningCause): string {\n\tswitch (cause.reason) {\n\t\tcase 'oauth-app-not-allowed':\n\t\t\treturn 'the organization does not allow third-party OAuth apps';\n\t\tcase 'access-denied':\n\t\t\treturn 'the account has no access to it';\n\t\tcase 'conditional-access':\n\t\t\treturn 'a Conditional Access policy blocked the request';\n\t\tcase 'unreachable':\n\t\t\treturn 'the provider server could not be reached';\n\t\tdefault:\n\t\t\tcause.reason satisfies never;\n\t\t\treturn 'the provider refused the request';\n\t}\n}\n\nfunction collectionFailureMessage(failure: ProviderScopeFailure): string {\n\tconst text = failure.cause != null ? causeText(failure.cause) : failure.message;\n\tconst detail = text != null ? `: ${text}` : '';\n\treturn `Failed to read ${failure.kind} scope${collectionScopeText(failure.scope)}${detail}`;\n}\n\n/**\n * Forwards the part of a failure's scope below the provider, so a consumer can tell \"one organization refused\n * this token\" from \"the connection's token is dead\" without parsing {@link collectionFailureMessage}.\n *\n * `providerId` is dropped: the warning already names its provider, and a scope naming nothing below it is a\n * failure of the connection itself. That is the distinction `getOrganizationsForUserResult` also draws before\n * counting an auth failure against the connection, though it reads a scope with no keys but `providerId`, so an\n * absent scope does not count there. Forwarding such a scope would make an account-wide failure look confined.\n *\n * Copied field by field, for the reason {@link toProviderWarningOmission} copies its scope.\n */\nfunction toProviderWarningScope(scope: CollectionScope | undefined): ProviderWarningScope | undefined {\n\tif (scope == null) return undefined;\n\n\tconst { resourceId, projectId, repositoryId } = scope;\n\tif (resourceId == null && projectId == null && repositoryId == null) return undefined;\n\n\treturn {\n\t\t...(resourceId != null ? { resourceId: resourceId } : {}),\n\t\t...(projectId != null ? { projectId: projectId } : {}),\n\t\t...(repositoryId != null ? { repositoryId: repositoryId } : {}),\n\t};\n}\n\n/**\n * Whether `metadata` reports authentication failures only for scopes below the provider, with none for the\n * connection itself. A credential revoked since a provider cached its discovery produces exactly this shape,\n * the same one a scope's own refusal does; `IntegrationBase.confirmScopedAuthFailures` tells them apart.\n *\n * Classified exactly as {@link assessCollectionMetadata} classifies the warnings, so it agrees with what a\n * consumer would be shown.\n */\nexport function hasOnlyScopedAuthFailures(metadata: CollectionMetadata | undefined): boolean {\n\tlet scoped = false;\n\tfor (const failure of metadata?.failures ?? []) {\n\t\tif (toCollectionFailureWarningKind(failure) !== 'auth') continue;\n\t\tif (toProviderWarningScope(failure.scope) == null || (failure as ProviderScopeFailure).credentialRefused) {\n\t\t\treturn false;\n\t\t}\n\n\t\tscoped = true;\n\t}\n\treturn scoped;\n}\n\n/** The scoped authentication failures in `metadata` that kept their refusal, with the scope they are forwarded as. */\nfunction* refusedScopes(\n\tmetadata: CollectionMetadata | undefined,\n): Generator<{ failure: ProviderScopeFailure & { refusal: ProviderRefusal }; scope: ProviderWarningScope }> {\n\tfor (const failure of (metadata?.failures ?? []) as ProviderScopeFailure[]) {\n\t\tif (\n\t\t\tfailure.refusal == null ||\n\t\t\tfailure.credentialRefused ||\n\t\t\ttoCollectionFailureWarningKind(failure) !== 'auth'\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst scope = toProviderWarningScope(failure.scope);\n\t\tif (scope == null) continue;\n\n\t\tyield { failure: failure as ProviderScopeFailure & { refusal: ProviderRefusal }, scope: scope };\n\t}\n}\n\n/**\n * Marks each scoped authentication failure in `metadata` whose refusal the provider pins on the credential, so it is\n * published for the connection rather than for its scope. Returns whether any was.\n */\nexport function markCredentialRefusals(\n\tmetadata: CollectionMetadata | undefined,\n\tisCredentialRefusal: (refusal: ProviderRefusal) => boolean,\n): boolean {\n\tlet marked = false;\n\tfor (const { failure } of refusedScopes(metadata)) {\n\t\tif (!isCredentialRefusal(failure.refusal)) continue;\n\n\t\tfailure.credentialRefused = true;\n\t\tmarked = true;\n\t}\n\treturn marked;\n}\n\n/**\n * Names the cause of each scoped authentication failure in `metadata` that kept its refusal. Only for a read whose\n * credential was just confirmed: before that, the refusals this can name look exactly like a dead credential.\n */\nexport function attributeScopedAuthFailures(\n\tmetadata: CollectionMetadata | undefined,\n\tdescribe: (refusal: ProviderRefusal, scope: ProviderWarningScope) => ProviderWarningCause | undefined,\n): void {\n\tfor (const { failure, scope } of refusedScopes(metadata)) {\n\t\tif (failure.cause != null) continue;\n\n\t\tconst cause = describe(failure.refusal, scope);\n\t\tif (cause != null) {\n\t\t\tfailure.cause = cause;\n\t\t}\n\t}\n}\n\n/**\n * Explains one omission in the consumer's terms — what was left out and, where the SDK reports it, how much.\n *\n * An omission is a completeness fact, never a failure: the read succeeded, and the provider (or the SDK's own\n * recovery budget) is what withheld results. That is why these never contribute to `fetchFailed`. Whether a\n * retry would recover anything is a separate question this layer cannot answer — see the `recovery` note on\n * {@link toProviderWarningOmission}.\n */\nfunction collectionOmissionMessage(omission: CollectionOmission): string {\n\tconst scopeText = collectionScopeText(omission.scope);\n\tswitch (omission.kind) {\n\t\tcase 'provider-limit':\n\t\t\t// `totalCount` is the only figure saying how much was withheld; both fields are optional, so degrade\n\t\t\t// through the shapes the SDK can actually emit rather than printing `undefined`.\n\t\t\tif (omission.totalCount != null && omission.limit != null) {\n\t\t\t\treturn `Search${scopeText} matched ${omission.totalCount} results, but the provider exposes at most ${omission.limit}`;\n\t\t\t}\n\t\t\tif (omission.limit != null) {\n\t\t\t\treturn `Search${scopeText} exceeded the provider limit of ${omission.limit} results`;\n\t\t\t}\n\t\t\treturn `Search${scopeText} exceeded the provider's result limit`;\n\t\tcase 'recovery-budget':\n\t\t\treturn `Stopped recovering omitted results${scopeText} after reaching the request budget${\n\t\t\t\tomission.limit != null ? ` of ${omission.limit} requests` : ''\n\t\t\t}`;\n\t\tcase 'pagination-incomplete':\n\t\t\treturn `More results are available${scopeText} than this read returned`;\n\t\tdefault:\n\t\t\t// `CollectionOmissionKind` is a closed union, so this is unreachable today. Kept as a compile-time\n\t\t\t// guard rather than a silent fallback: an SDK bump that adds a kind breaks the build here instead\n\t\t\t// of quietly degrading it to a vague message.\n\t\t\tomission.kind satisfies never;\n\t\t\treturn `Some results were omitted${scopeText}; the read is incomplete`;\n\t}\n}\n\n/**\n * Forwards an SDK omission as the structured signal consumers read instead of parsing {@link\n * collectionOmissionMessage}'s prose.\n *\n * `results.ts` re-spells `CollectionOmissionKind` rather than importing it, so the published warning surface\n * carries no `@gitkraken/provider-apis` types (see the export block in `index.ts`). The return type is what\n * keeps the two unions honest: an SDK bump that adds a member fails to compile here, alongside\n * `collectionOmissionMessage`'s `satisfies never`.\n *\n * `scope` is copied because the SDK's own object is retained and re-merged across drained pages\n * (`providerPaging.ts`), and this one crosses the package boundary.\n *\n * `recovery` is always `'none'`, deliberately, and a scoped `pagination-incomplete` is why it looks wrong: the\n * SDK emits that one shape from situations with opposite remedies and says so itself — \"Either the read\n * deliberately took one page per scope, or the provider advertised another page it gave no way to reach.\" Its\n * `collectAcrossScopes` producers (Azure, Bitbucket, Bitbucket Server) are the first, its `drainAcrossScopes`\n * producers (GitLab, Jira) the second, and those report an omission only when a cursor STALLED — so re-reading\n * that scope stalls at the identical page. Nothing in `CollectionOmission` separates the two, so claiming\n * recoverability would ship the dead-end button `recovery` exists to prevent. Making the recoverable case\n * claimable needs provider-apis to say whether a scope was drained or merely sampled: a fact to forward, not\n * to guess here.\n */\nfunction toProviderWarningOmission(omission: CollectionOmission): ProviderWarningOmission {\n\treturn {\n\t\tkind: omission.kind,\n\t\trecovery: 'none',\n\t\t...(omission.limit != null ? { limit: omission.limit } : {}),\n\t\t// The SDK types this `number | null | undefined`; normalize to one absence for consumers.\n\t\t...(omission.totalCount != null ? { totalCount: omission.totalCount } : {}),\n\t\t...(omission.scope != null ? { scope: { ...omission.scope } } : {}),\n\t};\n}\n\n/**\n * Whether SDK metadata describes a read that may be missing results.\n *\n * A reported omission counts on its own rather than deferring to `completeness`. The SDK does couple the\n * two — every site that emits an omission degrades completeness in the same call — so today the omission\n * clause changes no outcome. It is defensive: the two facts arrive as independent fields, and a read that\n * names what it left out must not be publishable as whole if a future producer sets only one of them.\n *\n * This is the single definition of \"may be missing results\", shared with `getPagedResult`, so one metadata\n * object cannot be truncated at one layer and complete at another.\n */\nexport function isIncompleteCollection(metadata: CollectionMetadata | undefined): boolean {\n\tif (metadata == null) return false;\n\n\treturn metadata.completeness !== 'complete' || (metadata.omissions?.length ?? 0) > 0;\n}\n\n/**\n * Converts internal SDK collection metadata into neutral provider facade signals.\n *\n * `requestFailed` is `fetchFailed` without the failures that only report items a read left unread: those fail the read,\n * since the items are missing, but every request answered, so the omissions the read reported still hold.\n */\nexport function assessCollectionMetadata(\n\tproviderId: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tmetadata: CollectionMetadata | undefined,\n): { warnings: ProviderWarning[]; fetchFailed: boolean; requestFailed: boolean; truncated: boolean } {\n\tif (metadata == null) return { warnings: [], fetchFailed: false, requestFailed: false, truncated: false };\n\n\tconst warnings: ProviderWarning[] = [];\n\tconst failures: ProviderScopeFailure[] = metadata.failures ?? [];\n\tfor (const failure of failures) {\n\t\tconst kind = toCollectionFailureWarningKind(failure);\n\t\tconst cause = toCollectionFailureCause(failure, kind);\n\t\tconst scope = failure.credentialRefused ? undefined : toProviderWarningScope(failure.scope);\n\t\tappendDedupedWarning(warnings, {\n\t\t\tproviderId: providerId,\n\t\t\tdomain: domain,\n\t\t\tconnectionId: connectionId,\n\t\t\t// A credential-level refusal names no scope in prose either, so every scope's copy of it is one warning.\n\t\t\tmessage: collectionFailureMessage({\n\t\t\t\t...failure,\n\t\t\t\tcause: cause,\n\t\t\t\t...(failure.credentialRefused ? { scope: undefined } : {}),\n\t\t\t}),\n\t\t\tkind: kind,\n\t\t\tisAuth: kind === 'auth',\n\t\t\t...(scope != null ? { scope: scope } : {}),\n\t\t\t// Copied for the reason the scope is: the failure object is retained and re-merged across pages.\n\t\t\t...(cause != null ? { cause: { ...cause } } : {}),\n\t\t\t...(failure.unreadableItems != null\n\t\t\t\t? {\n\t\t\t\t\t\tunreadableItems: {\n\t\t\t\t\t\t\tkeys: [...failure.unreadableItems.keys],\n\t\t\t\t\t\t\tcomplete: failure.unreadableItems.complete,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t}\n\n\t// Omissions explain WHY a read is incomplete when nothing failed — a provider cap, an exhausted recovery\n\t// budget, an undrained scope. They classify as `other`, never `auth`, and are deliberately excluded from\n\t// `fetchFailed` below: the request itself succeeded. `omission` carries that same fact structurally, so a\n\t// consumer can act on it without parsing the message.\n\tconst omissions = metadata.omissions ?? [];\n\tfor (const omission of omissions) {\n\t\tappendDedupedWarning(warnings, {\n\t\t\tproviderId: providerId,\n\t\t\tdomain: domain,\n\t\t\tconnectionId: connectionId,\n\t\t\tmessage: collectionOmissionMessage(omission),\n\t\t\tkind: 'other',\n\t\t\tisAuth: false,\n\t\t\tomission: toProviderWarningOmission(omission),\n\t\t});\n\t}\n\n\tconst incomplete = isIncompleteCollection(metadata);\n\t// Only fall back to the generic message when nothing more specific was reported; an omission already\n\t// explains the incompleteness in the consumer's terms, so adding this on top would be noise.\n\t//\n\t// This one deliberately carries no `omission`: it fires precisely when the SDK reported incompleteness\n\t// WITHOUT saying what was left out, so there is no structured fact to forward and synthesizing one would\n\t// assert a specificity this layer does not have.\n\tif (incomplete && failures.length === 0 && omissions.length === 0) {\n\t\tappendDedupedWarning(warnings, {\n\t\t\tproviderId: providerId,\n\t\t\tdomain: domain,\n\t\t\tconnectionId: connectionId,\n\t\t\tmessage:\n\t\t\t\tmetadata.completeness === 'partial'\n\t\t\t\t\t? 'Some results were omitted; the read is incomplete'\n\t\t\t\t\t: 'Result completeness could not be confirmed',\n\t\t\tkind: 'other',\n\t\t\tisAuth: false,\n\t\t});\n\t}\n\n\treturn {\n\t\twarnings: warnings,\n\t\tfetchFailed: failures.length > 0,\n\t\trequestFailed: failures.some(failure => failure.unreadableItems == null),\n\t\ttruncated: incomplete,\n\t};\n}\n\n/**\n * Appends neutral warnings derived from SDK metadata and returns its failure/truncation assessment.\n *\n * `reported` says whether the metadata described this read's incompleteness at all. A caller that would\n * otherwise add its own generic \"this was truncated\" warning uses it to stay quiet — the same rule\n * {@link assessCollectionMetadata} applies to its own generic fallback, one layer up: whatever the metadata\n * reported already names this provider and, unless it was that same fallback, says something a consumer can\n * act on. Restating it in a weaker vocabulary is how one gap becomes two warnings with two different verdicts.\n *\n * `truncated` is the near-synonym that must NOT be used for that question: it comes from\n * {@link isIncompleteCollection}, which does not consider failures, so metadata carrying a typed failure\n * alongside `completeness: 'complete'` reports `truncated: false` while having already appended the warning\n * that explains everything. Gating on it would restate exactly the case this field exists to keep quiet.\n */\nexport function mergeAssessmentInto(\n\twarnings: ProviderWarning[],\n\tproviderId: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tmetadata: CollectionMetadata | undefined,\n): { fetchFailed: boolean; truncated: boolean; reported: boolean } {\n\tconst assessment = assessCollectionMetadata(providerId, domain, connectionId, metadata);\n\tfor (const warning of assessment.warnings) {\n\t\tappendDedupedWarning(warnings, warning);\n\t}\n\treturn {\n\t\tfetchFailed: assessment.fetchFailed,\n\t\ttruncated: assessment.truncated,\n\t\t// What the assessment PRODUCED, not what `appendDedupedWarning` accepted: a warning dropped as a\n\t\t// duplicate is still one the consumer will see, so it still makes a restatement redundant.\n\t\treported: assessment.warnings.length > 0,\n\t};\n}\n"],"mappings":"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"}
@@ -62,6 +62,11 @@ export declare function isRateLimitResponse(ex: {
62
62
  export type ResponseHeaders = Pick<Headers, 'get'> | Record<string, unknown>;
63
63
  /** Reads one header off either shape of {@link ResponseHeaders}; names are the lower-case ones both shapes use. */
64
64
  export declare function getResponseHeader(headers: ResponseHeaders | undefined, name: string): unknown;
65
+ /**
66
+ * The user a Jira or Bitbucket Data Center response names in `X-AUSERNAME`: the user the request authenticated as,
67
+ * `anonymous` when its credential did not authenticate, or none (`undefined`) when the response carries no name.
68
+ */
69
+ export declare function getAuthenticatedUser(headers: ResponseHeaders | undefined): string | 'anonymous' | undefined;
65
70
  /**
66
71
  * Builds a {@link RequestRateLimitError} from a provider error response, reading the reset epoch from the
67
72
  * standard `x-ratelimit-reset` header when present. Accepts both header shapes in use: the `fetch` `Headers`
@@ -150,6 +150,15 @@ function getResponseHeader(headers, name) {
150
150
  return typeof headers.get === "function" ? headers.get(name) : headers[name];
151
151
  }
152
152
  /**
153
+ * The user a Jira or Bitbucket Data Center response names in `X-AUSERNAME`: the user the request authenticated as,
154
+ * `anonymous` when its credential did not authenticate, or none (`undefined`) when the response carries no name.
155
+ */
156
+ function getAuthenticatedUser(headers) {
157
+ const user = getResponseHeader(headers, "x-ausername");
158
+ if (typeof user !== "string" || user === "") return void 0;
159
+ return user.toLowerCase() === "anonymous" ? "anonymous" : user;
160
+ }
161
+ /**
153
162
  * Builds a {@link RequestRateLimitError} from a provider error response, reading the reset epoch from the
154
163
  * standard `x-ratelimit-reset` header when present. Accepts both header shapes in use: the `fetch` `Headers`
155
164
  * instance the direct clients get, and the plain object the SDK attaches to its errors.
@@ -173,6 +182,6 @@ function toError(ex) {
173
182
  return ex instanceof Error ? ex : new Error(String(ex));
174
183
  }
175
184
  //#endregion
176
- export { AuthenticationError, AuthenticationErrorReason, IntegrationReadUnavailableError, ProviderFetchError, RequestClientError, RequestNotFoundError, RequestRateLimitError, getResponseHeader, isProviderUnreachableError, isRateLimitResponse, toError, toRateLimitError };
185
+ export { AuthenticationError, AuthenticationErrorReason, IntegrationReadUnavailableError, ProviderFetchError, RequestClientError, RequestNotFoundError, RequestRateLimitError, getAuthenticatedUser, getResponseHeader, isProviderUnreachableError, isRateLimitResponse, toError, toRateLimitError };
177
186
 
178
187
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../../../../plus/integrations/src/errors.ts"],"sourcesContent":["export type { AuthTokenInfo } from '@gitlens/git/errors.js';\nexport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tRequestClientError,\n\tRequestNotFoundError,\n\tRequestRateLimitError,\n} from '@gitlens/git/errors.js';\n\nimport { RequestRateLimitError } from '@gitlens/git/errors.js';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\n\nconst unreachableErrorCodes = new Set([\n\t'ECONNREFUSED',\n\t'ECONNRESET',\n\t'ECONNABORTED',\n\t'ENOTFOUND',\n\t'EAI_AGAIN',\n\t'ETIMEDOUT',\n\t'EHOSTUNREACH',\n\t'ENETUNREACH',\n\t'EPIPE',\n\t'ERR_NETWORK',\n\t'UND_ERR_CONNECT_TIMEOUT',\n\t'UND_ERR_HEADERS_TIMEOUT',\n\t'UND_ERR_BODY_TIMEOUT',\n\t'UND_ERR_SOCKET',\n\t'CERT_HAS_EXPIRED',\n\t'DEPTH_ZERO_SELF_SIGNED_CERT',\n\t'SELF_SIGNED_CERT_IN_CHAIN',\n\t'UNABLE_TO_VERIFY_LEAF_SIGNATURE',\n\t'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',\n\t'ERR_TLS_CERT_ALTNAME_INVALID',\n]);\n\n/** Transport wrappers keep the network failure in `cause` or `original`; inspecting only the wrapper loses it. */\nexport function isProviderUnreachableError(ex: unknown): boolean {\n\tconst pending = [ex];\n\tconst seen = new Set<unknown>();\n\twhile (pending.length > 0) {\n\t\tconst candidate = pending.pop();\n\t\tif (candidate == null || typeof candidate !== 'object' || seen.has(candidate)) continue;\n\n\t\tseen.add(candidate);\n\t\tconst error = candidate as {\n\t\t\tname?: string;\n\t\t\tkind?: string;\n\t\t\tmessage?: string;\n\t\t\tcode?: string;\n\t\t\tstatus?: number;\n\t\t\tresponse?: { status?: number };\n\t\t\treason?: string;\n\t\t\toriginal?: unknown;\n\t\t\tcause?: unknown;\n\t\t\terrors?: unknown[];\n\t\t};\n\t\tif (isCancellationError(candidate)) {\n\t\t\tif (error.reason === 'aborted') return false;\n\t\t\tif (error.reason === 'timeout') return true;\n\t\t}\n\n\t\tconst status = error.status ?? error.response?.status;\n\t\tif (typeof status === 'number' && status >= 400) return status >= 500 && status < 600;\n\n\t\tif (\n\t\t\terror.kind === 'network' ||\n\t\t\terror.reason === 'unreachable' ||\n\t\t\terror.name === 'TimeoutError' ||\n\t\t\terror.name === 'AbortError' ||\n\t\t\terror.name === 'NetworkError' ||\n\t\t\t(typeof error.code === 'string' &&\n\t\t\t\t(unreachableErrorCodes.has(error.code) || error.code.startsWith('ERR_SSL_'))) ||\n\t\t\t(typeof error.message === 'string' &&\n\t\t\t\t/^(?:fetch failed|failed to fetch|network request failed|load failed|NetworkError when attempting to fetch resource\\.?)$/i.test(\n\t\t\t\t\terror.message,\n\t\t\t\t))\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\tpending.push(error.original, error.cause);\n\t\tif (Array.isArray(error.errors)) {\n\t\t\tpending.push(...error.errors);\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Thrown by a provider read when it can't produce a result for a reason that is NOT \"the account is empty\":\n * a prerequisite is missing (e.g. Trello has no app key) or a required scope can't be resolved (e.g. the\n * Linear viewer id, needed to scope \"my issues\"). Returning an empty/undefined result in these cases would be\n * indistinguishable from a genuinely empty account; throwing this lets the result cores recover it into an\n * `{ error }` the facade surfaces as a warning + `fetchFailed`.\n */\nexport class IntegrationReadUnavailableError extends Error {\n\tconstructor(provider: string, reason: string) {\n\t\tsuper(`${provider} read unavailable: ${reason}`);\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\n/**\n * Thrown by integration provider HTTP clients when an upstream request fails.\n */\nexport class ProviderFetchError extends Error {\n\tget status(): number {\n\t\treturn this.response.status;\n\t}\n\n\tget statusText(): string {\n\t\treturn this.response.statusText;\n\t}\n\n\t/**\n\t * Builds the error with the upstream body folded into the message, for clients that hold a `Response` whose\n\t * body has not been read (i.e. every non-`ok` path). Use this rather than the constructor whenever the body is\n\t * still available: the status line alone cannot distinguish a throttled request from a permission failure on\n\t * the statuses hosts overload (see {@link isRateLimitResponse}), and only the body says which one it is.\n\t *\n\t * Never throws: the body is best-effort, so an unreadable, empty, or already-consumed one simply yields the\n\t * status-line-only message the constructor would have produced.\n\t */\n\tstatic async fromResponse(provider: string, response: Response): Promise<ProviderFetchError> {\n\t\treturn new ProviderFetchError(provider, response, await readErrorBodyMessage(response));\n\t}\n\n\tconstructor(\n\t\tprovider: string,\n\t\tpublic readonly response: Response,\n\t\terrors?: { message: string }[],\n\t) {\n\t\tsuper(\n\t\t\t`${provider} request failed: ${!response.ok ? `(${response.status}) ${response.statusText}. ` : ''}${\n\t\t\t\terrors?.length ? errors[0].message : ''\n\t\t\t}`,\n\t\t);\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\n/** Cap on the body text folded into an error message, so an HTML error page can't become the message. */\nconst maxErrorBodyLength = 500;\n\n/**\n * Best-effort human-readable message from an error response body, in the `errors` shape the\n * {@link ProviderFetchError} constructor takes. Tolerant by design — every host in use words its errors\n * differently and some do not send JSON at all:\n * - Azure DevOps — `{ message: \"TF400733: ...\" }`\n * - Bitbucket — `{ error: { message: \"...\" } }`, and bare text for a throttled request\n * - GitLab — `{ message: \"...\" }` or `{ error: \"...\" }`\n * Anything unrecognized falls back to the raw text, which is what the substring check wants anyway.\n */\nasync function readErrorBodyMessage(response: Response): Promise<{ message: string }[] | undefined> {\n\tlet text;\n\ttry {\n\t\t// `bodyUsed` is not a guarantee (a stream can fail mid-read), hence the catch — but it avoids the throw\n\t\t// entirely on the common path where something upstream already consumed the body.\n\t\tif (response.bodyUsed) return undefined;\n\n\t\ttext = (await response.text())?.trim();\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!text) return undefined;\n\n\tlet message: string | undefined;\n\ttry {\n\t\tconst body: unknown = JSON.parse(text);\n\t\tif (body != null && typeof body === 'object') {\n\t\t\tconst { message: m, error } = body as { message?: unknown; error?: unknown };\n\t\t\tif (typeof m === 'string') {\n\t\t\t\tmessage = m;\n\t\t\t} else if (typeof error === 'string') {\n\t\t\t\tmessage = error;\n\t\t\t} else if (error != null && typeof error === 'object') {\n\t\t\t\t// Some hosts nest it as `{ error: { message } }`; narrow the same way as the outer object.\n\t\t\t\tconst nested = (error as { message?: unknown }).message;\n\t\t\t\tif (typeof nested === 'string') {\n\t\t\t\t\tmessage = nested;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Not JSON — Bitbucket sends bare text for a throttled request, and any host can send an HTML error page.\n\t}\n\n\tmessage = (message ?? text).trim();\n\treturn message ? [{ message: message.slice(0, maxErrorBodyLength) }] : undefined;\n}\n\n/**\n * Whether a provider's error response denotes a rate limit rather than a permission failure. Kept here, in one\n * place, because the distinction is consequential: a rate limit is retryable, while a permission failure is\n * surfaced as `auth` and drives a reconnect prompt — misclassifying one as the other asks the user to\n * re-authenticate a perfectly healthy connection.\n *\n * Which status a throttled request arrives on varies by host, which is why this takes both:\n * - Azure DevOps and Bitbucket Cloud document 429 (learn.microsoft.com .../integrate/concepts/rate-limits).\n * - GitHub documents \"a `403` or `429` response\" for both its primary and secondary limits\n * (docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api).\n * - GitLab uses 403 for some throttles.\n * So 429 alone is decisive, and on 403 the message decides.\n *\n * NOTE the message must actually carry the host's body for the 403 path to work — see\n * {@link ProviderFetchError.fromResponse}. A `ProviderFetchError` built from the status line alone reads\n * \"(403) Forbidden.\" and can never match, which is what made this check dead code on the direct-fetch clients.\n */\nexport function isRateLimitResponse(ex: { status: number; message?: string }): boolean {\n\t// Matches the phrasing hosts actually use: GitHub/Bitbucket \"API rate limit exceeded\", GitLab \"rate limit\n\t// exceeded\", Azure \"Request was blocked due to exceeding usage of resource 'RateLimit'\" / \"TF400733: The\n\t// request has been canceled: Request was blocked due to exceeding usage\".\n\tif (ex.status === 429) return true;\n\n\tconst message = ex.message?.toLowerCase();\n\treturn message != null && (message.includes('rate limit') || message.includes('ratelimit'));\n}\n\n/**\n * A response's headers as either a `fetch` `Headers` instance or the plain object an SDK error carries. Read via\n * duck-typing on `get` rather than `instanceof Headers`, so this works in both the Node and webworker builds\n * without depending on the global being present.\n */\nexport type ResponseHeaders = Pick<Headers, 'get'> | Record<string, unknown>;\n\n/** Reads one header off either shape of {@link ResponseHeaders}; names are the lower-case ones both shapes use. */\nexport function getResponseHeader(headers: ResponseHeaders | undefined, name: string): unknown {\n\tif (headers == null) return undefined;\n\n\treturn typeof headers.get === 'function'\n\t\t? (headers.get as (name: string) => string | null)(name)\n\t\t: (headers as Record<string, unknown>)[name];\n}\n\n/**\n * Builds a {@link RequestRateLimitError} from a provider error response, reading the reset epoch from the\n * standard `x-ratelimit-reset` header when present. Accepts both header shapes in use: the `fetch` `Headers`\n * instance the direct clients get, and the plain object the SDK attaches to its errors.\n */\nexport function toRateLimitError(\n\tex: Error & { response?: { headers?: ResponseHeaders } },\n\ttoken: string | undefined,\n): RequestRateLimitError {\n\tconst raw = getResponseHeader(ex.response?.headers, 'x-ratelimit-reset');\n\n\t// Only a header that actually came back as a string/number can be a reset epoch; anything else (a nested SDK\n\t// object, an array of values) would stringify to garbage and parse to NaN anyway.\n\tlet resetAt: number | undefined;\n\tif (typeof raw === 'string' || typeof raw === 'number') {\n\t\tresetAt = parseInt(String(raw), 10);\n\t\tif (Number.isNaN(resetAt)) {\n\t\t\tresetAt = undefined;\n\t\t}\n\t}\n\n\treturn new RequestRateLimitError(ex, token, resetAt);\n}\n\n/**\n * Normalizes a caught `unknown` into an `Error`. Result cores type their failure channel as `error: Error`,\n * but a `catch` binding is `unknown` and a provider (or a third-party SDK) can throw a non-Error value;\n * returning it raw would leak a non-Error through the public result surface and break downstream `instanceof`\n * classification. Passes an existing `Error` through unchanged and wraps anything else with `String(ex)`.\n */\nexport function toError(ex: unknown): Error {\n\treturn ex instanceof Error ? ex : new Error(String(ex));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../../../../plus/integrations/src/errors.ts"],"sourcesContent":["export type { AuthTokenInfo } from '@gitlens/git/errors.js';\nexport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tRequestClientError,\n\tRequestNotFoundError,\n\tRequestRateLimitError,\n} from '@gitlens/git/errors.js';\n\nimport { RequestRateLimitError } from '@gitlens/git/errors.js';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\n\nconst unreachableErrorCodes = new Set([\n\t'ECONNREFUSED',\n\t'ECONNRESET',\n\t'ECONNABORTED',\n\t'ENOTFOUND',\n\t'EAI_AGAIN',\n\t'ETIMEDOUT',\n\t'EHOSTUNREACH',\n\t'ENETUNREACH',\n\t'EPIPE',\n\t'ERR_NETWORK',\n\t'UND_ERR_CONNECT_TIMEOUT',\n\t'UND_ERR_HEADERS_TIMEOUT',\n\t'UND_ERR_BODY_TIMEOUT',\n\t'UND_ERR_SOCKET',\n\t'CERT_HAS_EXPIRED',\n\t'DEPTH_ZERO_SELF_SIGNED_CERT',\n\t'SELF_SIGNED_CERT_IN_CHAIN',\n\t'UNABLE_TO_VERIFY_LEAF_SIGNATURE',\n\t'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',\n\t'ERR_TLS_CERT_ALTNAME_INVALID',\n]);\n\n/** Transport wrappers keep the network failure in `cause` or `original`; inspecting only the wrapper loses it. */\nexport function isProviderUnreachableError(ex: unknown): boolean {\n\tconst pending = [ex];\n\tconst seen = new Set<unknown>();\n\twhile (pending.length > 0) {\n\t\tconst candidate = pending.pop();\n\t\tif (candidate == null || typeof candidate !== 'object' || seen.has(candidate)) continue;\n\n\t\tseen.add(candidate);\n\t\tconst error = candidate as {\n\t\t\tname?: string;\n\t\t\tkind?: string;\n\t\t\tmessage?: string;\n\t\t\tcode?: string;\n\t\t\tstatus?: number;\n\t\t\tresponse?: { status?: number };\n\t\t\treason?: string;\n\t\t\toriginal?: unknown;\n\t\t\tcause?: unknown;\n\t\t\terrors?: unknown[];\n\t\t};\n\t\tif (isCancellationError(candidate)) {\n\t\t\tif (error.reason === 'aborted') return false;\n\t\t\tif (error.reason === 'timeout') return true;\n\t\t}\n\n\t\tconst status = error.status ?? error.response?.status;\n\t\tif (typeof status === 'number' && status >= 400) return status >= 500 && status < 600;\n\n\t\tif (\n\t\t\terror.kind === 'network' ||\n\t\t\terror.reason === 'unreachable' ||\n\t\t\terror.name === 'TimeoutError' ||\n\t\t\terror.name === 'AbortError' ||\n\t\t\terror.name === 'NetworkError' ||\n\t\t\t(typeof error.code === 'string' &&\n\t\t\t\t(unreachableErrorCodes.has(error.code) || error.code.startsWith('ERR_SSL_'))) ||\n\t\t\t(typeof error.message === 'string' &&\n\t\t\t\t/^(?:fetch failed|failed to fetch|network request failed|load failed|NetworkError when attempting to fetch resource\\.?)$/i.test(\n\t\t\t\t\terror.message,\n\t\t\t\t))\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\tpending.push(error.original, error.cause);\n\t\tif (Array.isArray(error.errors)) {\n\t\t\tpending.push(...error.errors);\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Thrown by a provider read when it can't produce a result for a reason that is NOT \"the account is empty\":\n * a prerequisite is missing (e.g. Trello has no app key) or a required scope can't be resolved (e.g. the\n * Linear viewer id, needed to scope \"my issues\"). Returning an empty/undefined result in these cases would be\n * indistinguishable from a genuinely empty account; throwing this lets the result cores recover it into an\n * `{ error }` the facade surfaces as a warning + `fetchFailed`.\n */\nexport class IntegrationReadUnavailableError extends Error {\n\tconstructor(provider: string, reason: string) {\n\t\tsuper(`${provider} read unavailable: ${reason}`);\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\n/**\n * Thrown by integration provider HTTP clients when an upstream request fails.\n */\nexport class ProviderFetchError extends Error {\n\tget status(): number {\n\t\treturn this.response.status;\n\t}\n\n\tget statusText(): string {\n\t\treturn this.response.statusText;\n\t}\n\n\t/**\n\t * Builds the error with the upstream body folded into the message, for clients that hold a `Response` whose\n\t * body has not been read (i.e. every non-`ok` path). Use this rather than the constructor whenever the body is\n\t * still available: the status line alone cannot distinguish a throttled request from a permission failure on\n\t * the statuses hosts overload (see {@link isRateLimitResponse}), and only the body says which one it is.\n\t *\n\t * Never throws: the body is best-effort, so an unreadable, empty, or already-consumed one simply yields the\n\t * status-line-only message the constructor would have produced.\n\t */\n\tstatic async fromResponse(provider: string, response: Response): Promise<ProviderFetchError> {\n\t\treturn new ProviderFetchError(provider, response, await readErrorBodyMessage(response));\n\t}\n\n\tconstructor(\n\t\tprovider: string,\n\t\tpublic readonly response: Response,\n\t\terrors?: { message: string }[],\n\t) {\n\t\tsuper(\n\t\t\t`${provider} request failed: ${!response.ok ? `(${response.status}) ${response.statusText}. ` : ''}${\n\t\t\t\terrors?.length ? errors[0].message : ''\n\t\t\t}`,\n\t\t);\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\n/** Cap on the body text folded into an error message, so an HTML error page can't become the message. */\nconst maxErrorBodyLength = 500;\n\n/**\n * Best-effort human-readable message from an error response body, in the `errors` shape the\n * {@link ProviderFetchError} constructor takes. Tolerant by design — every host in use words its errors\n * differently and some do not send JSON at all:\n * - Azure DevOps — `{ message: \"TF400733: ...\" }`\n * - Bitbucket — `{ error: { message: \"...\" } }`, and bare text for a throttled request\n * - GitLab — `{ message: \"...\" }` or `{ error: \"...\" }`\n * Anything unrecognized falls back to the raw text, which is what the substring check wants anyway.\n */\nasync function readErrorBodyMessage(response: Response): Promise<{ message: string }[] | undefined> {\n\tlet text;\n\ttry {\n\t\t// `bodyUsed` is not a guarantee (a stream can fail mid-read), hence the catch — but it avoids the throw\n\t\t// entirely on the common path where something upstream already consumed the body.\n\t\tif (response.bodyUsed) return undefined;\n\n\t\ttext = (await response.text())?.trim();\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!text) return undefined;\n\n\tlet message: string | undefined;\n\ttry {\n\t\tconst body: unknown = JSON.parse(text);\n\t\tif (body != null && typeof body === 'object') {\n\t\t\tconst { message: m, error } = body as { message?: unknown; error?: unknown };\n\t\t\tif (typeof m === 'string') {\n\t\t\t\tmessage = m;\n\t\t\t} else if (typeof error === 'string') {\n\t\t\t\tmessage = error;\n\t\t\t} else if (error != null && typeof error === 'object') {\n\t\t\t\t// Some hosts nest it as `{ error: { message } }`; narrow the same way as the outer object.\n\t\t\t\tconst nested = (error as { message?: unknown }).message;\n\t\t\t\tif (typeof nested === 'string') {\n\t\t\t\t\tmessage = nested;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Not JSON — Bitbucket sends bare text for a throttled request, and any host can send an HTML error page.\n\t}\n\n\tmessage = (message ?? text).trim();\n\treturn message ? [{ message: message.slice(0, maxErrorBodyLength) }] : undefined;\n}\n\n/**\n * Whether a provider's error response denotes a rate limit rather than a permission failure. Kept here, in one\n * place, because the distinction is consequential: a rate limit is retryable, while a permission failure is\n * surfaced as `auth` and drives a reconnect prompt — misclassifying one as the other asks the user to\n * re-authenticate a perfectly healthy connection.\n *\n * Which status a throttled request arrives on varies by host, which is why this takes both:\n * - Azure DevOps and Bitbucket Cloud document 429 (learn.microsoft.com .../integrate/concepts/rate-limits).\n * - GitHub documents \"a `403` or `429` response\" for both its primary and secondary limits\n * (docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api).\n * - GitLab uses 403 for some throttles.\n * So 429 alone is decisive, and on 403 the message decides.\n *\n * NOTE the message must actually carry the host's body for the 403 path to work — see\n * {@link ProviderFetchError.fromResponse}. A `ProviderFetchError` built from the status line alone reads\n * \"(403) Forbidden.\" and can never match, which is what made this check dead code on the direct-fetch clients.\n */\nexport function isRateLimitResponse(ex: { status: number; message?: string }): boolean {\n\t// Matches the phrasing hosts actually use: GitHub/Bitbucket \"API rate limit exceeded\", GitLab \"rate limit\n\t// exceeded\", Azure \"Request was blocked due to exceeding usage of resource 'RateLimit'\" / \"TF400733: The\n\t// request has been canceled: Request was blocked due to exceeding usage\".\n\tif (ex.status === 429) return true;\n\n\tconst message = ex.message?.toLowerCase();\n\treturn message != null && (message.includes('rate limit') || message.includes('ratelimit'));\n}\n\n/**\n * A response's headers as either a `fetch` `Headers` instance or the plain object an SDK error carries. Read via\n * duck-typing on `get` rather than `instanceof Headers`, so this works in both the Node and webworker builds\n * without depending on the global being present.\n */\nexport type ResponseHeaders = Pick<Headers, 'get'> | Record<string, unknown>;\n\n/** Reads one header off either shape of {@link ResponseHeaders}; names are the lower-case ones both shapes use. */\nexport function getResponseHeader(headers: ResponseHeaders | undefined, name: string): unknown {\n\tif (headers == null) return undefined;\n\n\treturn typeof headers.get === 'function'\n\t\t? (headers.get as (name: string) => string | null)(name)\n\t\t: (headers as Record<string, unknown>)[name];\n}\n\n/**\n * The user a Jira or Bitbucket Data Center response names in `X-AUSERNAME`: the user the request authenticated as,\n * `anonymous` when its credential did not authenticate, or none (`undefined`) when the response carries no name.\n */\nexport function getAuthenticatedUser(headers: ResponseHeaders | undefined): string | 'anonymous' | undefined {\n\tconst user = getResponseHeader(headers, 'x-ausername');\n\tif (typeof user !== 'string' || user === '') return undefined;\n\n\treturn user.toLowerCase() === 'anonymous' ? 'anonymous' : user;\n}\n\n/**\n * Builds a {@link RequestRateLimitError} from a provider error response, reading the reset epoch from the\n * standard `x-ratelimit-reset` header when present. Accepts both header shapes in use: the `fetch` `Headers`\n * instance the direct clients get, and the plain object the SDK attaches to its errors.\n */\nexport function toRateLimitError(\n\tex: Error & { response?: { headers?: ResponseHeaders } },\n\ttoken: string | undefined,\n): RequestRateLimitError {\n\tconst raw = getResponseHeader(ex.response?.headers, 'x-ratelimit-reset');\n\n\t// Only a header that actually came back as a string/number can be a reset epoch; anything else (a nested SDK\n\t// object, an array of values) would stringify to garbage and parse to NaN anyway.\n\tlet resetAt: number | undefined;\n\tif (typeof raw === 'string' || typeof raw === 'number') {\n\t\tresetAt = parseInt(String(raw), 10);\n\t\tif (Number.isNaN(resetAt)) {\n\t\t\tresetAt = undefined;\n\t\t}\n\t}\n\n\treturn new RequestRateLimitError(ex, token, resetAt);\n}\n\n/**\n * Normalizes a caught `unknown` into an `Error`. Result cores type their failure channel as `error: Error`,\n * but a `catch` binding is `unknown` and a provider (or a third-party SDK) can throw a non-Error value;\n * returning it raw would leak a non-Error through the public result surface and break downstream `instanceof`\n * classification. Passes an existing `Error` through unchanged and wraps anything else with `String(ex)`.\n */\nexport function toError(ex: unknown): Error {\n\treturn ex instanceof Error ? ex : new Error(String(ex));\n}\n"],"mappings":"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"}
@@ -6,7 +6,7 @@ import { AuthenticationSessionLike, CloudIntegrationAuthType, CloudIntegrationCo
6
6
  import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, IntegrationIds, IssuesCloudHostIntegrationId, IssuesSelfManagedHostIntegrationId, SupportedCloudIntegrationIds, isIntegrationId, isSupportedCloudIntegrationId } from "./constants.js";
7
7
  import { Source, Sources } from "./telemetry.js";
8
8
  import { ConfiguredIntegrationsChangeEvent } from "./authentication/configuredIntegrationService.js";
9
- import { ConnectionStateChangeEvent, ProviderBroadenResult, ProviderOrganization, ProviderPageInfo, ProviderPagedResult, ProviderRepositoryShape, ProviderResult, ProviderSweepResult, ProviderWarning, ProviderWarningCause, ProviderWarningCauseReason, ProviderWarningKind, ProviderWarningOmission, ProviderWarningOmissionKind, ProviderWarningOmissionRecovery, ProviderWarningOmissionScope, ProviderWarningScope, ProviderWarningUnreadableItems, RepositoryIdentity, RepositoryResolution, RepositoryResolutionStatus, ResolveRepositoryResult } from "./results.js";
9
+ import { ConnectionStateChangeEvent, ProviderBroadenResult, ProviderOrganization, ProviderPageInfo, ProviderPagedResult, ProviderRepositoryShape, ProviderResult, ProviderSweepResult, ProviderWarning, ProviderWarningCause, ProviderWarningCauseReason, ProviderWarningKind, ProviderWarningOmission, ProviderWarningOmissionKind, ProviderWarningOmissionRecovery, ProviderWarningOmissionScope, ProviderWarningScope, ProviderWarningSelector, ProviderWarningUnreadableItems, RepositoryIdentity, RepositoryResolution, RepositoryResolutionStatus, ResolveRepositoryResult } from "./results.js";
10
10
  import { IssueFilter } from "./providerFilters.js";
11
11
  import { IssueEtagInclude, PullRequestEtagInclude } from "./models/integration.js";
12
12
  import { IssueCountResult, IssueCountScope, PullRequestCountResult, PullRequestCountScope } from "./reads/counts.js";
@@ -78,5 +78,5 @@ export interface IntegrationManagerContext {
78
78
  */
79
79
  export declare function createIntegrationManager(ctx: IntegrationManagerContext): IntegrationManager;
80
80
  //#endregion
81
- export { type Account, type AccountProvider, type AuthenticationSessionLike, type AuthenticationSessionsChangeEvent, type ClosedPullRequestSweepOptions, type CloudIntegrationAuthType, type CloudIntegrationConnection, type CloudIntegrationType, type ConfigChangeEvent, type ConfigProvider, type ConfiguredIntegrationDescriptor, type ConfiguredIntegrationsChangeEvent, type ConnectionStateChangeEvent, type CurrentAccountResult, type FieldPresence, GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, type HttpProvider, type IntegrationAuthenticationProvider, type IntegrationAuthenticationProviderDescriptor, type IntegrationAuthenticationSessionDescriptor, type IntegrationIds, type IntegrationManager, type IntegrationServiceHooks, type IntegrationStorageProvider, type IntegrationsRemoteConfig, type IssueBatchResult, type IssueBatchTarget, type IssueCountResult, type IssueCountScope, type IssueEtagInclude, type IssueFieldGroup, IssueFilter, type IssueProjection, type IssueSearchCapabilities, type IssueSearchCriteria, type IssueSearchRelationship, type IssueStateFilter, IssuesCloudHostIntegrationId, IssuesSelfManagedHostIntegrationId, type ListOrgsOptions, type ListProjectsOptions, type ManualTokenAuthProviderOptions, type ProviderAuthenticationSession, type ProviderBroadenOrg, type ProviderBroadenResult, type ProviderOrganization, type ProviderPageInfo, type ProviderPagedResult, type ProviderRepositoriesInput, type ProviderRepositoryInput, type ProviderRepositoryShape, type ProviderResult, type ProviderSweepResult, type ProviderSweepTarget, type ProviderSweepTargetEvent, type ProviderWarning, type ProviderWarningCause, type ProviderWarningCauseReason, type ProviderWarningKind, type ProviderWarningOmission, type ProviderWarningOmissionKind, type ProviderWarningOmissionRecovery, type ProviderWarningOmissionScope, type ProviderWarningScope, type ProviderWarningUnreadableItems, type PullRequestBatchResult, type PullRequestBatchTarget, type PullRequestBranchResult, type PullRequestBranchTarget, type PullRequestCountResult, type PullRequestCountScope, type PullRequestEtagInclude, type PullRequestFieldGroup, PullRequestFilter, type PullRequestProjection, type PullRequestSearchCapabilities, type PullRequestSearchCriteria, type PullRequestStateFilter, type PullRequestSweepOptions, type RepositoriesProvider, type RepositoryIdentity, type RepositoryResolution, type RepositoryResolutionStatus, type ResolveRepositoryResult, type Source, type Sources, type SupportedCloudIntegrationIds, areDomainsOnSameHost, createManualTokenAuthProvider, getIssueFieldPresence, getPullRequestFieldPresence, hostFromDomain, isIntegrationId, isSupportedCloudIntegrationId, toCloudIntegrationType, toIntegrationId };
81
+ export { type Account, type AccountProvider, type AuthenticationSessionLike, type AuthenticationSessionsChangeEvent, type ClosedPullRequestSweepOptions, type CloudIntegrationAuthType, type CloudIntegrationConnection, type CloudIntegrationType, type ConfigChangeEvent, type ConfigProvider, type ConfiguredIntegrationDescriptor, type ConfiguredIntegrationsChangeEvent, type ConnectionStateChangeEvent, type CurrentAccountResult, type FieldPresence, GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, type HttpProvider, type IntegrationAuthenticationProvider, type IntegrationAuthenticationProviderDescriptor, type IntegrationAuthenticationSessionDescriptor, type IntegrationIds, type IntegrationManager, type IntegrationServiceHooks, type IntegrationStorageProvider, type IntegrationsRemoteConfig, type IssueBatchResult, type IssueBatchTarget, type IssueCountResult, type IssueCountScope, type IssueEtagInclude, type IssueFieldGroup, IssueFilter, type IssueProjection, type IssueSearchCapabilities, type IssueSearchCriteria, type IssueSearchRelationship, type IssueStateFilter, IssuesCloudHostIntegrationId, IssuesSelfManagedHostIntegrationId, type ListOrgsOptions, type ListProjectsOptions, type ManualTokenAuthProviderOptions, type ProviderAuthenticationSession, type ProviderBroadenOrg, type ProviderBroadenResult, type ProviderOrganization, type ProviderPageInfo, type ProviderPagedResult, type ProviderRepositoriesInput, type ProviderRepositoryInput, type ProviderRepositoryShape, type ProviderResult, type ProviderSweepResult, type ProviderSweepTarget, type ProviderSweepTargetEvent, type ProviderWarning, type ProviderWarningCause, type ProviderWarningCauseReason, type ProviderWarningKind, type ProviderWarningOmission, type ProviderWarningOmissionKind, type ProviderWarningOmissionRecovery, type ProviderWarningOmissionScope, type ProviderWarningScope, type ProviderWarningSelector, type ProviderWarningUnreadableItems, type PullRequestBatchResult, type PullRequestBatchTarget, type PullRequestBranchResult, type PullRequestBranchTarget, type PullRequestCountResult, type PullRequestCountScope, type PullRequestEtagInclude, type PullRequestFieldGroup, PullRequestFilter, type PullRequestProjection, type PullRequestSearchCapabilities, type PullRequestSearchCriteria, type PullRequestStateFilter, type PullRequestSweepOptions, type RepositoriesProvider, type RepositoryIdentity, type RepositoryResolution, type RepositoryResolutionStatus, type ResolveRepositoryResult, type Source, type Sources, type SupportedCloudIntegrationIds, areDomainsOnSameHost, createManualTokenAuthProvider, getIssueFieldPresence, getPullRequestFieldPresence, hostFromDomain, isIntegrationId, isSupportedCloudIntegrationId, toCloudIntegrationType, toIntegrationId };
82
82
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../plus/integrations/src/index.ts"],"sourcesContent":["// Public facade for `@gitlens/integrations`.\n//\n// External consumers should import from this entry point only, not from the\n// package's internal subpaths, with one exception: the lightweight, token-scoped\n// read API is a supported public subpath (`./lite.js`). It's kept out of this\n// facade on purpose so importing the session-managed manager doesn't eagerly pull\n// in every provider API client. The internal classes (IntegrationService,\n// IntegrationAuthenticationService, etc.) are not part of the public API and\n// may be refactored without semver bumps.\n\nimport type { Account } from '@gitlens/git/models/author.js';\nimport { CacheController } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationIds } from './constants.js';\nimport type {\n\tAccountProvider,\n\tConfigProvider,\n\tHttpProvider,\n\tIntegrationCacheProvider,\n\tIntegrationServiceHooks,\n\tIntegrationStorageProvider,\n\tRepositoriesProvider,\n} from './context.js';\nimport { createIntegrationService } from './integrationService.js';\nimport type { IntegrationManager } from './manager.js';\n\n/** A cached provider value, which may still be resolving. */\nexport type IntegrationManagerCacheResult<T> = Promise<T | undefined> | T | undefined;\n\n/** Produces an account value on a cache miss. */\nexport type IntegrationManagerCacheLoader<T> = (cacheable: CacheController) => {\n\tvalue: IntegrationManagerCacheResult<T>;\n\texpiresAt?: number;\n};\n\n/** Stable integration identity supplied to the consumer-owned account cache. */\nexport interface IntegrationAccountCacheDescriptor {\n\treadonly id: IntegrationIds;\n\treadonly domain?: string;\n}\n\n/** Cache controls attached to a provider account lookup. */\nexport interface IntegrationManagerCacheOptions {\n\treadonly connectionId?: string;\n\treadonly etag?: string;\n\treadonly expiryOverride?: boolean | number;\n\treadonly expireOnError?: boolean;\n}\n\n/**\n * Optional consumer-owned cache for values reused by the public manager facade.\n *\n * The manager only needs cross-call caching for provider identity lookups. Other caches used by the GitLens\n * extension host are implementation details and deliberately stay out of this contract.\n */\nexport interface IntegrationManagerCacheProvider {\n\tgetCurrentAccount(\n\t\tintegration: IntegrationAccountCacheDescriptor,\n\t\tcacheable: IntegrationManagerCacheLoader<Account>,\n\t\toptions?: IntegrationManagerCacheOptions,\n\t): IntegrationManagerCacheResult<Account>;\n}\n\nfunction loadUncached<T>(loader: IntegrationManagerCacheLoader<T>): IntegrationManagerCacheResult<T> {\n\treturn loader(new CacheController()).value;\n}\n\nconst uncachedIntegrationCacheProvider: IntegrationCacheProvider = {\n\tgetRepositoryMetadata: (_repo, _integration, loader) => loadUncached(loader),\n\tgetRepositoryDefaultBranch: (_repo, _integration, loader) => loadUncached(loader),\n\tgetPullRequestForSha: (_sha, _repo, _integration, loader) => loadUncached(loader),\n\tgetPullRequestForBranch: (_branch, _repo, _integration, loader) => loadUncached(loader),\n\tgetPullRequest: (_id, _resource, _integration, loader) => loadUncached(loader),\n\tgetIssueOrPullRequest: (_id, _type, _resource, _integration, loader) => loadUncached(loader),\n\tgetIssue: (_id, _resource, _integration, loader) => loadUncached(loader),\n\tgetCurrentAccount: (_integration, loader) => loadUncached(loader),\n\t// Nothing is cached, so there is nothing to evict.\n\tdeletePullRequests: () => {},\n};\n\nfunction toIntegrationCacheProvider(cache: IntegrationManagerCacheProvider | undefined): IntegrationCacheProvider {\n\tif (cache == null) return uncachedIntegrationCacheProvider;\n\n\treturn {\n\t\t...uncachedIntegrationCacheProvider,\n\t\tgetCurrentAccount: (integration, loader, options) =>\n\t\t\tcache.getCurrentAccount({ id: integration.id, domain: integration.domain }, loader, options),\n\t};\n}\n\n/**\n * Consumer-facing runtime for {@link createIntegrationManager}.\n *\n * External consumers may omit `cache`; reads then execute their loaders directly without cross-call caching.\n * The extension host's broader integration context remains private to the implementation.\n */\nexport interface IntegrationManagerContext {\n\treadonly storage: IntegrationStorageProvider;\n\treadonly account: AccountProvider;\n\treadonly config: ConfigProvider;\n\treadonly http: HttpProvider;\n\treadonly cache?: IntegrationManagerCacheProvider;\n\treadonly repositories: RepositoriesProvider;\n\treadonly hooks?: IntegrationServiceHooks;\n}\n\nexport type {\n\tClosedPullRequestSweepOptions,\n\tIntegrationManager,\n\tListOrgsOptions,\n\tListProjectsOptions,\n\t// Every option shape a caller has to BUILD is exported, not just the ones the manager returns:\n\t// `manager.js` is not a public subpath, so a type omitted here can't be named by a consumer at all\n\t// (`broadenIssues`' `orgs` needed `ProviderBroadenOrg` and had to be re-declared downstream).\n\tProviderBroadenOrg,\n\tProviderRepositoriesInput,\n\tProviderRepositoryInput,\n\tProviderSweepTarget,\n\tProviderSweepTargetEvent,\n\tPullRequestSweepOptions,\n} from './manager.js';\n\n/**\n * Construct an `@gitlens/integrations` manager bound to the supplied runtime.\n *\n * The runtime is the **single** cross-boundary contract — anything the\n * package needs (HTTP, storage, cache, auth, subscription, telemetry, UI,\n * configuration, environment) flows through it. The package never imports\n * `vscode`, `Container`, or any consumer-internal types.\n *\n * The returned manager owns its internal services; dispose it (or its\n * containing scope) to release every cached integration plus the runtime's\n * own VS Code subscriptions.\n */\nexport function createIntegrationManager(ctx: IntegrationManagerContext): IntegrationManager {\n\treturn createIntegrationService({\n\t\t...ctx,\n\t\tcache: toIntegrationCacheProvider(ctx.cache),\n\t});\n}\n\n// Re-exports for the public API surface.\nexport type {\n\tAccountProvider,\n\tAuthenticationSessionsChangeEvent,\n\tConfigChangeEvent,\n\tConfigProvider,\n\tRepositoriesProvider,\n\tHttpProvider,\n\tIntegrationsRemoteConfig,\n\tIntegrationServiceHooks,\n\tIntegrationStorageProvider,\n} from './context.js';\nexport type { Source, Sources } from './telemetry.js';\nexport type { IntegrationIds, SupportedCloudIntegrationIds } from './constants.js';\nexport type { ConfiguredIntegrationsChangeEvent } from './authentication/configuredIntegrationService.js';\n\n// Authentication contract — what `IntegrationServiceHooks.createAuthenticationProvider`\n// implementers return. Consumers can plug in any auth strategy (manual token,\n// OAuth, host-managed) by returning an object implementing this interface from\n// the hook.\nexport type {\n\tIntegrationAuthenticationProvider,\n\tIntegrationAuthenticationProviderDescriptor,\n\tIntegrationAuthenticationSessionDescriptor,\n} from './authentication/integrationAuthenticationProvider.js';\nexport type {\n\tAuthenticationSessionLike,\n\tCloudIntegrationAuthType,\n\tCloudIntegrationConnection,\n\tCloudIntegrationType,\n\tConfiguredIntegrationDescriptor,\n\tProviderAuthenticationSession,\n} from './authentication/models.js';\n// Provider-id mapping helpers for consumers bridging their own provider ids to `IntegrationIds`\n// (e.g. mapping multi-account connections from `getConfigured` back to a provider) and vice versa.\nexport { toCloudIntegrationType, toIntegrationId } from './authentication/models.js';\n// Domain normalization is part of connection selection and repository resolution; consumers should use the\n// same implementation rather than importing an internal utility subpath or maintaining a divergent copy.\nexport { areDomainsOnSameHost, hostFromDomain } from './utils/domain.utils.js';\n\n// Convenience: wrap a static access token (env var, CLI flag, secret manager)\n// as an `IntegrationAuthenticationProvider`. For OAuth/refresh flows, implement\n// the interface directly — this helper is for non-interactive consumers only.\nexport {\n\tcreateManualTokenAuthProvider,\n\ttype ManualTokenAuthProviderOptions,\n} from './authentication/manualTokenProvider.js';\n\nexport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n\tIssuesSelfManagedHostIntegrationId,\n\tisIntegrationId,\n\tisSupportedCloudIntegrationId,\n} from './constants.js';\n\n// Neutral pagination + warning result types the Kepler ProviderBackend adapter maps to its own DTOs.\n// These carry no `@gitkraken/provider-apis` types, so consumers depend only on `@gitkraken/core-gitlens`.\nexport type {\n\tConnectionStateChangeEvent,\n\tProviderBroadenResult,\n\tProviderPagedResult,\n\tProviderPageInfo,\n\tProviderResult,\n\tProviderSweepResult,\n\tProviderWarning,\n\tProviderWarningCause,\n\tProviderWarningCauseReason,\n\tProviderWarningKind,\n\tProviderWarningOmission,\n\tProviderWarningOmissionKind,\n\tProviderWarningOmissionRecovery,\n\tProviderWarningOmissionScope,\n\tProviderWarningScope,\n\tProviderWarningUnreadableItems,\n\tProviderOrganization,\n\tProviderRepositoryShape,\n\tRepositoryIdentity,\n\tRepositoryResolution,\n\tRepositoryResolutionStatus,\n\tResolveRepositoryResult,\n} from './results.js';\n// Runtime enums — re-exported as values (not `export type`) so consumers can read their members.\nexport { IssueFilter, PullRequestFilter } from './providerFilters.js';\n// The filtered issue search's criteria model and the capability table that says which of it a provider honors.\n// String unions and plain interfaces, so a consumer builds criteria without importing an internal subpath.\nexport type { IssueSearchCapabilities, IssueSearchCriteria, IssueSearchRelationship } from './providerFilters.js';\n// Account-wide pull-request search criteria and the per-provider capability table.\nexport type { PullRequestSearchCapabilities, PullRequestSearchCriteria } from './providerFilters.js';\n// The count-only issue and pull-request probes' input and result shapes.\nexport type {\n\tIssueCountResult,\n\tIssueCountScope,\n\tPullRequestCountResult,\n\tPullRequestCountScope,\n} from './reads/counts.js';\nexport type { IssueBatchResult, IssueBatchTarget } from './reads/issueBatch.js';\nexport type { PullRequestBatchResult, PullRequestBatchTarget } from './reads/pullRequestBatch.js';\nexport type { IssueEtagInclude, PullRequestEtagInclude } from './models/integration.js';\nexport type { PullRequestBranchResult, PullRequestBranchTarget } from './reads/pullRequestBranches.js';\n// The current-account read's result, and the account shape every identity-bearing read already returns.\nexport type { CurrentAccountResult } from './reads/currentAccount.js';\nexport type { Account } from '@gitlens/git/models/author.js';\n// Cross-provider PR/issue state filters (string unions in the git models).\nexport type { PullRequestStateFilter } from '@gitlens/git/models/pullRequest.js';\nexport type { IssueStateFilter } from '@gitlens/git/models/issue.js';\n// Which read produced a row, and which of its fields that read fetched.\nexport type { PullRequestProjection } from '@gitlens/git/models/pullRequest.js';\nexport type { IssueProjection } from '@gitlens/git/models/issue.js';\nexport type { FieldPresence, IssueFieldGroup, PullRequestFieldGroup } from './fieldPresence.js';\nexport { getIssueFieldPresence, getPullRequestFieldPresence } from './fieldPresence.js';\n"],"mappings":";;;;;;;;;;AA8DA,SAAS,aAAgB,QAA4E;CACpG,OAAO,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC;AACtC;AAEA,MAAM,mCAA6D;CAClE,wBAAwB,OAAO,cAAc,WAAW,aAAa,MAAM;CAC3E,6BAA6B,OAAO,cAAc,WAAW,aAAa,MAAM;CAChF,uBAAuB,MAAM,OAAO,cAAc,WAAW,aAAa,MAAM;CAChF,0BAA0B,SAAS,OAAO,cAAc,WAAW,aAAa,MAAM;CACtF,iBAAiB,KAAK,WAAW,cAAc,WAAW,aAAa,MAAM;CAC7E,wBAAwB,KAAK,OAAO,WAAW,cAAc,WAAW,aAAa,MAAM;CAC3F,WAAW,KAAK,WAAW,cAAc,WAAW,aAAa,MAAM;CACvE,oBAAoB,cAAc,WAAW,aAAa,MAAM;CAEhE,0BAA0B,CAAC;AAC5B;AAEA,SAAS,2BAA2B,OAA8E;CACjH,IAAI,SAAS,MAAM,OAAO;CAE1B,OAAO;EACN,GAAG;EACH,oBAAoB,aAAa,QAAQ,YACxC,MAAM,kBAAkB;GAAE,IAAI,YAAY;GAAI,QAAQ,YAAY;EAAO,GAAG,QAAQ,OAAO;CAC7F;AACD;;;;;;;;;;;;;AA8CA,SAAgB,yBAAyB,KAAoD;CAC5F,OAAO,yBAAyB;EAC/B,GAAG;EACH,OAAO,2BAA2B,IAAI,KAAK;CAC5C,CAAC;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../plus/integrations/src/index.ts"],"sourcesContent":["// Public facade for `@gitlens/integrations`.\n//\n// External consumers should import from this entry point only, not from the\n// package's internal subpaths, with one exception: the lightweight, token-scoped\n// read API is a supported public subpath (`./lite.js`). It's kept out of this\n// facade on purpose so importing the session-managed manager doesn't eagerly pull\n// in every provider API client. The internal classes (IntegrationService,\n// IntegrationAuthenticationService, etc.) are not part of the public API and\n// may be refactored without semver bumps.\n\nimport type { Account } from '@gitlens/git/models/author.js';\nimport { CacheController } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationIds } from './constants.js';\nimport type {\n\tAccountProvider,\n\tConfigProvider,\n\tHttpProvider,\n\tIntegrationCacheProvider,\n\tIntegrationServiceHooks,\n\tIntegrationStorageProvider,\n\tRepositoriesProvider,\n} from './context.js';\nimport { createIntegrationService } from './integrationService.js';\nimport type { IntegrationManager } from './manager.js';\n\n/** A cached provider value, which may still be resolving. */\nexport type IntegrationManagerCacheResult<T> = Promise<T | undefined> | T | undefined;\n\n/** Produces an account value on a cache miss. */\nexport type IntegrationManagerCacheLoader<T> = (cacheable: CacheController) => {\n\tvalue: IntegrationManagerCacheResult<T>;\n\texpiresAt?: number;\n};\n\n/** Stable integration identity supplied to the consumer-owned account cache. */\nexport interface IntegrationAccountCacheDescriptor {\n\treadonly id: IntegrationIds;\n\treadonly domain?: string;\n}\n\n/** Cache controls attached to a provider account lookup. */\nexport interface IntegrationManagerCacheOptions {\n\treadonly connectionId?: string;\n\treadonly etag?: string;\n\treadonly expiryOverride?: boolean | number;\n\treadonly expireOnError?: boolean;\n}\n\n/**\n * Optional consumer-owned cache for values reused by the public manager facade.\n *\n * The manager only needs cross-call caching for provider identity lookups. Other caches used by the GitLens\n * extension host are implementation details and deliberately stay out of this contract.\n */\nexport interface IntegrationManagerCacheProvider {\n\tgetCurrentAccount(\n\t\tintegration: IntegrationAccountCacheDescriptor,\n\t\tcacheable: IntegrationManagerCacheLoader<Account>,\n\t\toptions?: IntegrationManagerCacheOptions,\n\t): IntegrationManagerCacheResult<Account>;\n}\n\nfunction loadUncached<T>(loader: IntegrationManagerCacheLoader<T>): IntegrationManagerCacheResult<T> {\n\treturn loader(new CacheController()).value;\n}\n\nconst uncachedIntegrationCacheProvider: IntegrationCacheProvider = {\n\tgetRepositoryMetadata: (_repo, _integration, loader) => loadUncached(loader),\n\tgetRepositoryDefaultBranch: (_repo, _integration, loader) => loadUncached(loader),\n\tgetPullRequestForSha: (_sha, _repo, _integration, loader) => loadUncached(loader),\n\tgetPullRequestForBranch: (_branch, _repo, _integration, loader) => loadUncached(loader),\n\tgetPullRequest: (_id, _resource, _integration, loader) => loadUncached(loader),\n\tgetIssueOrPullRequest: (_id, _type, _resource, _integration, loader) => loadUncached(loader),\n\tgetIssue: (_id, _resource, _integration, loader) => loadUncached(loader),\n\tgetCurrentAccount: (_integration, loader) => loadUncached(loader),\n\t// Nothing is cached, so there is nothing to evict.\n\tdeletePullRequests: () => {},\n};\n\nfunction toIntegrationCacheProvider(cache: IntegrationManagerCacheProvider | undefined): IntegrationCacheProvider {\n\tif (cache == null) return uncachedIntegrationCacheProvider;\n\n\treturn {\n\t\t...uncachedIntegrationCacheProvider,\n\t\tgetCurrentAccount: (integration, loader, options) =>\n\t\t\tcache.getCurrentAccount({ id: integration.id, domain: integration.domain }, loader, options),\n\t};\n}\n\n/**\n * Consumer-facing runtime for {@link createIntegrationManager}.\n *\n * External consumers may omit `cache`; reads then execute their loaders directly without cross-call caching.\n * The extension host's broader integration context remains private to the implementation.\n */\nexport interface IntegrationManagerContext {\n\treadonly storage: IntegrationStorageProvider;\n\treadonly account: AccountProvider;\n\treadonly config: ConfigProvider;\n\treadonly http: HttpProvider;\n\treadonly cache?: IntegrationManagerCacheProvider;\n\treadonly repositories: RepositoriesProvider;\n\treadonly hooks?: IntegrationServiceHooks;\n}\n\nexport type {\n\tClosedPullRequestSweepOptions,\n\tIntegrationManager,\n\tListOrgsOptions,\n\tListProjectsOptions,\n\t// Every option shape a caller has to BUILD is exported, not just the ones the manager returns:\n\t// `manager.js` is not a public subpath, so a type omitted here can't be named by a consumer at all\n\t// (`broadenIssues`' `orgs` needed `ProviderBroadenOrg` and had to be re-declared downstream).\n\tProviderBroadenOrg,\n\tProviderRepositoriesInput,\n\tProviderRepositoryInput,\n\tProviderSweepTarget,\n\tProviderSweepTargetEvent,\n\tPullRequestSweepOptions,\n} from './manager.js';\n\n/**\n * Construct an `@gitlens/integrations` manager bound to the supplied runtime.\n *\n * The runtime is the **single** cross-boundary contract — anything the\n * package needs (HTTP, storage, cache, auth, subscription, telemetry, UI,\n * configuration, environment) flows through it. The package never imports\n * `vscode`, `Container`, or any consumer-internal types.\n *\n * The returned manager owns its internal services; dispose it (or its\n * containing scope) to release every cached integration plus the runtime's\n * own VS Code subscriptions.\n */\nexport function createIntegrationManager(ctx: IntegrationManagerContext): IntegrationManager {\n\treturn createIntegrationService({\n\t\t...ctx,\n\t\tcache: toIntegrationCacheProvider(ctx.cache),\n\t});\n}\n\n// Re-exports for the public API surface.\nexport type {\n\tAccountProvider,\n\tAuthenticationSessionsChangeEvent,\n\tConfigChangeEvent,\n\tConfigProvider,\n\tRepositoriesProvider,\n\tHttpProvider,\n\tIntegrationsRemoteConfig,\n\tIntegrationServiceHooks,\n\tIntegrationStorageProvider,\n} from './context.js';\nexport type { Source, Sources } from './telemetry.js';\nexport type { IntegrationIds, SupportedCloudIntegrationIds } from './constants.js';\nexport type { ConfiguredIntegrationsChangeEvent } from './authentication/configuredIntegrationService.js';\n\n// Authentication contract — what `IntegrationServiceHooks.createAuthenticationProvider`\n// implementers return. Consumers can plug in any auth strategy (manual token,\n// OAuth, host-managed) by returning an object implementing this interface from\n// the hook.\nexport type {\n\tIntegrationAuthenticationProvider,\n\tIntegrationAuthenticationProviderDescriptor,\n\tIntegrationAuthenticationSessionDescriptor,\n} from './authentication/integrationAuthenticationProvider.js';\nexport type {\n\tAuthenticationSessionLike,\n\tCloudIntegrationAuthType,\n\tCloudIntegrationConnection,\n\tCloudIntegrationType,\n\tConfiguredIntegrationDescriptor,\n\tProviderAuthenticationSession,\n} from './authentication/models.js';\n// Provider-id mapping helpers for consumers bridging their own provider ids to `IntegrationIds`\n// (e.g. mapping multi-account connections from `getConfigured` back to a provider) and vice versa.\nexport { toCloudIntegrationType, toIntegrationId } from './authentication/models.js';\n// Domain normalization is part of connection selection and repository resolution; consumers should use the\n// same implementation rather than importing an internal utility subpath or maintaining a divergent copy.\nexport { areDomainsOnSameHost, hostFromDomain } from './utils/domain.utils.js';\n\n// Convenience: wrap a static access token (env var, CLI flag, secret manager)\n// as an `IntegrationAuthenticationProvider`. For OAuth/refresh flows, implement\n// the interface directly — this helper is for non-interactive consumers only.\nexport {\n\tcreateManualTokenAuthProvider,\n\ttype ManualTokenAuthProviderOptions,\n} from './authentication/manualTokenProvider.js';\n\nexport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n\tIssuesSelfManagedHostIntegrationId,\n\tisIntegrationId,\n\tisSupportedCloudIntegrationId,\n} from './constants.js';\n\n// Neutral pagination + warning result types the Kepler ProviderBackend adapter maps to its own DTOs.\n// These carry no `@gitkraken/provider-apis` types, so consumers depend only on `@gitkraken/core-gitlens`.\nexport type {\n\tConnectionStateChangeEvent,\n\tProviderBroadenResult,\n\tProviderPagedResult,\n\tProviderPageInfo,\n\tProviderResult,\n\tProviderSweepResult,\n\tProviderWarning,\n\tProviderWarningCause,\n\tProviderWarningCauseReason,\n\tProviderWarningKind,\n\tProviderWarningOmission,\n\tProviderWarningOmissionKind,\n\tProviderWarningOmissionRecovery,\n\tProviderWarningOmissionScope,\n\tProviderWarningScope,\n\tProviderWarningSelector,\n\tProviderWarningUnreadableItems,\n\tProviderOrganization,\n\tProviderRepositoryShape,\n\tRepositoryIdentity,\n\tRepositoryResolution,\n\tRepositoryResolutionStatus,\n\tResolveRepositoryResult,\n} from './results.js';\n// Runtime enums — re-exported as values (not `export type`) so consumers can read their members.\nexport { IssueFilter, PullRequestFilter } from './providerFilters.js';\n// The filtered issue search's criteria model and the capability table that says which of it a provider honors.\n// String unions and plain interfaces, so a consumer builds criteria without importing an internal subpath.\nexport type { IssueSearchCapabilities, IssueSearchCriteria, IssueSearchRelationship } from './providerFilters.js';\n// Account-wide pull-request search criteria and the per-provider capability table.\nexport type { PullRequestSearchCapabilities, PullRequestSearchCriteria } from './providerFilters.js';\n// The count-only issue and pull-request probes' input and result shapes.\nexport type {\n\tIssueCountResult,\n\tIssueCountScope,\n\tPullRequestCountResult,\n\tPullRequestCountScope,\n} from './reads/counts.js';\nexport type { IssueBatchResult, IssueBatchTarget } from './reads/issueBatch.js';\nexport type { PullRequestBatchResult, PullRequestBatchTarget } from './reads/pullRequestBatch.js';\nexport type { IssueEtagInclude, PullRequestEtagInclude } from './models/integration.js';\nexport type { PullRequestBranchResult, PullRequestBranchTarget } from './reads/pullRequestBranches.js';\n// The current-account read's result, and the account shape every identity-bearing read already returns.\nexport type { CurrentAccountResult } from './reads/currentAccount.js';\nexport type { Account } from '@gitlens/git/models/author.js';\n// Cross-provider PR/issue state filters (string unions in the git models).\nexport type { PullRequestStateFilter } from '@gitlens/git/models/pullRequest.js';\nexport type { IssueStateFilter } from '@gitlens/git/models/issue.js';\n// Which read produced a row, and which of its fields that read fetched.\nexport type { PullRequestProjection } from '@gitlens/git/models/pullRequest.js';\nexport type { IssueProjection } from '@gitlens/git/models/issue.js';\nexport type { FieldPresence, IssueFieldGroup, PullRequestFieldGroup } from './fieldPresence.js';\nexport { getIssueFieldPresence, getPullRequestFieldPresence } from './fieldPresence.js';\n"],"mappings":";;;;;;;;;;AA8DA,SAAS,aAAgB,QAA4E;CACpG,OAAO,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC;AACtC;AAEA,MAAM,mCAA6D;CAClE,wBAAwB,OAAO,cAAc,WAAW,aAAa,MAAM;CAC3E,6BAA6B,OAAO,cAAc,WAAW,aAAa,MAAM;CAChF,uBAAuB,MAAM,OAAO,cAAc,WAAW,aAAa,MAAM;CAChF,0BAA0B,SAAS,OAAO,cAAc,WAAW,aAAa,MAAM;CACtF,iBAAiB,KAAK,WAAW,cAAc,WAAW,aAAa,MAAM;CAC7E,wBAAwB,KAAK,OAAO,WAAW,cAAc,WAAW,aAAa,MAAM;CAC3F,WAAW,KAAK,WAAW,cAAc,WAAW,aAAa,MAAM;CACvE,oBAAoB,cAAc,WAAW,aAAa,MAAM;CAEhE,0BAA0B,CAAC;AAC5B;AAEA,SAAS,2BAA2B,OAA8E;CACjH,IAAI,SAAS,MAAM,OAAO;CAE1B,OAAO;EACN,GAAG;EACH,oBAAoB,aAAa,QAAQ,YACxC,MAAM,kBAAkB;GAAE,IAAI,YAAY;GAAI,QAAQ,YAAY;EAAO,GAAG,QAAQ,OAAO;CAC7F;AACD;;;;;;;;;;;;;AA8CA,SAAgB,yBAAyB,KAAoD;CAC5F,OAAO,yBAAyB;EAC/B,GAAG;EACH,OAAO,2BAA2B,IAAI,KAAK;CAC5C,CAAC;AACF"}
@@ -67,9 +67,13 @@ export declare abstract class GitHostIntegration<ID extends IntegrationIds = Int
67
67
  }>;
68
68
  /**
69
69
  * {@link resolveRepositoryCoordinates} for the repository descriptors the list and search reads take: those, unless
70
- * the host overrides both.
70
+ * the host overrides both. A scope flagged `unsettled` that then reads nothing is reported as a failure of the read
71
+ * ({@link toUnsettledScopeFailure}) rather than as an empty scope.
71
72
  */
72
- protected resolveRepositoryInputs(_session: ProviderAuthenticationSession, repos: readonly ProviderRepoInput[], _cancellation?: AbortSignal): Promise<ProviderRepoInput[]>;
73
+ protected resolveRepositoryInputs(_session: ProviderAuthenticationSession, repos: readonly ProviderRepoInput[], _cancellation?: AbortSignal): Promise<{
74
+ repos: ProviderRepoInput[];
75
+ unsettled?: readonly boolean[];
76
+ }>;
73
77
  /**
74
78
  * The repositories a repo-scoped read of `repos` addresses ({@link resolveRepositoryInputs}), for a caller that
75
79
  * shapes the read by them before making it. Resolves the session as that read does, and fails as it would: a
@@ -11,7 +11,7 @@ import "../../../git/models/pullRequest.js";
11
11
  import { toError } from "../errors.js";
12
12
  import "../constants.js";
13
13
  import { isAzureProviderId } from "../providers/providerErrors.js";
14
- import { throwIfCallerContractError, toCollectionFailureError, toCollectionScopeFailure } from "../collectionMetadata.js";
14
+ import { throwIfCallerContractError, toCollectionFailureError, toCollectionScopeFailure, toUnsettledScopeFailure, unsettledMissMessage } from "../collectionMetadata.js";
15
15
  import { toTokenWithInfo } from "../authentication/models.js";
16
16
  import "../providerFilters.js";
17
17
  import { areDomainsOnSameHost } from "../utils/domain.utils.js";
@@ -39,10 +39,17 @@ function rejectUnsettledMisses(slots, unsettled, isMiss) {
39
39
  if (!unsettled?.some(Boolean)) return slots;
40
40
  return slots.map((slot, i) => unsettled[i] && slot.status === "fulfilled" && isMiss(slot.value) ? {
41
41
  status: "rejected",
42
- reason: /* @__PURE__ */ new Error("Nothing read for a repository that couldn't be settled")
42
+ reason: new Error(unsettledMissMessage)
43
43
  } : slot);
44
44
  }
45
45
  /**
46
+ * The keys of the scopes {@link GitHostIntegration.resolveRepositoryInputs} couldn't settle, for a list read to record
47
+ * a miss for one as a failure ({@link toUnsettledScopeFailure}) rather than as an empty scope.
48
+ */
49
+ function toUnsettledScopeKeys(repos, unsettled, key) {
50
+ return new Set(unsettled?.some(Boolean) ? repos.filter((_, i) => unsettled[i]).map(key) : []);
51
+ }
52
+ /**
46
53
  * Rejects a read the provider can't serve as asked: logs the reason and returns it as the `{ error }` half of an
47
54
  * {@link IntegrationResult}, timed from `start`.
48
55
  *
@@ -94,10 +101,11 @@ var GitHostIntegration = class extends IntegrationBase {
94
101
  }
95
102
  /**
96
103
  * {@link resolveRepositoryCoordinates} for the repository descriptors the list and search reads take: those, unless
97
- * the host overrides both.
104
+ * the host overrides both. A scope flagged `unsettled` that then reads nothing is reported as a failure of the read
105
+ * ({@link toUnsettledScopeFailure}) rather than as an empty scope.
98
106
  */
99
107
  resolveRepositoryInputs(_session, repos, _cancellation) {
100
- return Promise.resolve([...repos]);
108
+ return Promise.resolve({ repos: [...repos] });
101
109
  }
102
110
  /**
103
111
  * The repositories a repo-scoped read of `repos` addresses ({@link resolveRepositoryInputs}), for a caller that
@@ -111,7 +119,7 @@ var GitHostIntegration = class extends IntegrationBase {
111
119
  const session = await this.resolveReadSession(connectionId, void 0);
112
120
  if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
113
121
  try {
114
- return { value: await this.resolveRepositoryInputs(session, repos) };
122
+ return { value: (await this.resolveRepositoryInputs(session, repos)).repos };
115
123
  } catch {
116
124
  return { value: repos };
117
125
  }
@@ -538,10 +546,12 @@ var GitHostIntegration = class extends IntegrationBase {
538
546
  if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
539
547
  const start = performance.now();
540
548
  let resolvedRepos;
549
+ let unsettled;
541
550
  let customUrl;
542
551
  let api;
543
552
  try {
544
- resolvedRepos = isRepoIdsInput(repos) ? repos : await this.resolveRepositoryInputs(session, repos);
553
+ if (isRepoIdsInput(repos)) resolvedRepos = repos;
554
+ else ({repos: resolvedRepos, unsettled} = await this.resolveRepositoryInputs(session, repos));
545
555
  customUrl = options?.customUrl ?? (isGitSelfManagedHostIntegrationId(providerId) ? this.getRepositoriesApiBaseUrl(session, resolvedRepos) : void 0);
546
556
  api = await this.getProvidersApi();
547
557
  } catch (ex) {
@@ -592,6 +602,7 @@ var GitHostIntegration = class extends IntegrationBase {
592
602
  mentionLogin: options.filters.includes("mention") ? userFilterProperty : void 0
593
603
  };
594
604
  }
605
+ const unsettledProjects = toUnsettledScopeKeys(reposOrRepoIds, unsettled, (r) => `${r.namespace}\0${r.project}`);
595
606
  const cursors = this.parseCursorInfo(options?.cursor).cursors ?? [];
596
607
  let projectInputs = Array.from(projects.values(), (project) => ({
597
608
  namespace: organization,
@@ -642,6 +653,17 @@ var GitHostIntegration = class extends IntegrationBase {
642
653
  data.push(...results.values);
643
654
  if (results.paging?.truncated) truncated = true;
644
655
  metadata = mergeCollectionMetadata(metadata, results.metadata);
656
+ if (projectInput.cursor == null && results.values.length === 0 && unsettledProjects.has(`${projectInput.namespace}\0${projectInput.project}`)) {
657
+ truncated = true;
658
+ metadata = mergeCollectionMetadata(metadata, {
659
+ completeness: "partial",
660
+ failures: [toUnsettledScopeFailure({
661
+ providerId,
662
+ resourceId: projectInput.namespace,
663
+ projectId: projectInput.project
664
+ })]
665
+ });
666
+ }
645
667
  if (results.paging?.more) {
646
668
  hasMore = true;
647
669
  cursor.cursors.push({
@@ -871,10 +893,12 @@ var GitHostIntegration = class extends IntegrationBase {
871
893
  if (session == null || isReadSessionFailure(session)) return session && { error: session.error };
872
894
  const start = performance.now();
873
895
  let resolvedRepos;
896
+ let unsettled;
874
897
  let customUrl;
875
898
  let api;
876
899
  try {
877
- resolvedRepos = isRepoIdsInput(repos) ? repos : await this.resolveRepositoryInputs(session, repos);
900
+ if (isRepoIdsInput(repos)) resolvedRepos = repos;
901
+ else ({repos: resolvedRepos, unsettled} = await this.resolveRepositoryInputs(session, repos));
878
902
  customUrl = options?.customUrl ?? (isGitSelfManagedHostIntegrationId(providerId) ? this.getRepositoriesApiBaseUrl(session, resolvedRepos) : void 0);
879
903
  api = await this.getProvidersApi();
880
904
  } catch (ex) {
@@ -963,6 +987,8 @@ var GitHostIntegration = class extends IntegrationBase {
963
987
  };
964
988
  }
965
989
  if (api.getProviderPullRequestsPagingMode(providerId) === "repo" && !api.isRepoIdsInput(reposOrRepoIds)) {
990
+ const toRepoInputKey = (r) => `${r.namespace}\0${r.project ?? ""}\0${r.name}`;
991
+ const unsettledRepos = toUnsettledScopeKeys(reposOrRepoIds, unsettled, toRepoInputKey);
966
992
  const cursors = this.parseCursorInfo(options?.cursor).cursors ?? [];
967
993
  let repoInputs = reposOrRepoIds.map((repo) => ({
968
994
  repo,
@@ -1004,6 +1030,10 @@ var GitHostIntegration = class extends IntegrationBase {
1004
1030
  const { repoInput, results } = outcome.value;
1005
1031
  data.push(...results.values);
1006
1032
  metadata = mergeCollectionMetadata(metadata, results.metadata);
1033
+ if (repoInput.cursor == null && results.values.length === 0 && unsettledRepos.has(toRepoInputKey(repoInput.repo))) failures.push(toUnsettledScopeFailure({
1034
+ providerId,
1035
+ repositoryId: `${repoInput.repo.namespace}/${repoInput.repo.name}`
1036
+ }));
1007
1037
  if (results.paging?.truncated) truncated = true;
1008
1038
  if (results.paging?.more) {
1009
1039
  hasMore = true;