@gitkraken/core-gitlens 0.19.0 → 0.20.1

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.20.1] - 2026-10-26
10
+
11
+ ### Changed
12
+
13
+ - Imports `EntityIdentifierUtils` and `GitProviderUtils` from the `@gitkraken/provider-apis` main entry instead of its `/entity-identifiers` and `/provider-utils` subpaths. Those subpaths are standalone bundles that repeat code the main entry already ships, and core already loads the main entry, so a host that bundles core carried both copies. Same functions and behavior; a Node ESM consumer still links both names through the main entry's CommonJS export list ([#6086](https://github.com/gitkraken/vscode-gitlens/issues/6086)) (plus/integrations)
14
+
15
+ ## [0.20.0] - 2026-10-26
16
+
17
+ ### Added
18
+
19
+ - Adds `requestFailed?: boolean` and `unexpectedGap?: boolean` to `ProviderPagedResult`, so a host can tell why a paged read is incomplete without reading its warnings. `requestFailed` is `fetchFailed` without the failures that only name items a successful read left unreadable, which those items' own warnings already report. `unexpectedGap` is set when the provider promised another page without a usable cursor or page position, or repeated the cursor it was given, including on a page walked internally to reach a requested page number. Both are set to an explicit boolean by `listPullRequestsPage`, `listIssuesPage` and `listIssueTrackerIssuesPage`, so their absence means a read that doesn't report them yet ([gitkraken/kepler#4398](https://github.com/gitkraken/kepler/issues/4398)) (plus/integrations)
20
+
9
21
  ## [0.19.0] - 2026-10-26
10
22
 
11
23
  ### Added
@@ -968,7 +980,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
968
980
 
969
981
  - 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.
970
982
 
971
- [unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.19.0...HEAD
983
+ [unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.20.1...HEAD
984
+ [0.20.1]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.20.0...gitkraken:releases/core/v0.20.1
985
+ [0.20.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.19.0...gitkraken:releases/core/v0.20.0
972
986
  [0.19.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.18.0...gitkraken:releases/core/v0.19.0
973
987
  [0.18.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.17.0...gitkraken:releases/core/v0.18.0
974
988
  [0.17.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.16.5...gitkraken:releases/core/v0.17.0
@@ -153,6 +153,7 @@ export declare function assessCollectionMetadata(providerId: IntegrationIds, dom
153
153
  */
154
154
  export declare function mergeAssessmentInto(warnings: ProviderWarning[], providerId: IntegrationIds, domain: string | undefined, connectionId: string | undefined, metadata: CollectionMetadata | undefined): {
155
155
  fetchFailed: boolean;
156
+ requestFailed: boolean;
156
157
  truncated: boolean;
157
158
  reported: boolean;
158
159
  };
@@ -445,6 +445,7 @@ function mergeAssessmentInto(warnings, providerId, domain, connectionId, metadat
445
445
  for (const warning of assessment.warnings) appendDedupedWarning(warnings, warning);
446
446
  return {
447
447
  fetchFailed: assessment.fetchFailed,
448
+ requestFailed: assessment.requestFailed,
448
449
  truncated: assessment.truncated,
449
450
  reported: assessment.warnings.length > 0
450
451
  };
@@ -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\tCollectionSkippedItems,\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} from './results.js';\nimport { appendDedupedWarning, getRequestFailureCause, 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}\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\tconst cause = kind === 'provider' ? getRequestFailureCause(ex) : 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...(cause != null ? { cause: cause } : {}),\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, which is not\n\t// the page's. A failure that reports skipped items is never classified by its message.\n\tif (failure.skippedItems != 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\tcase 'unexpected-response':\n\t\t\treturn 'the provider returned an unexpected response';\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 * A failure's `skippedItems` as a well-formed one, whatever the SDK's payload held. Without an array of `keys` it names\n * none; with one it keeps the string keys, without duplicates and in order. It counts at least the keys it names, and\n * is `complete` only when it says so and nothing contradicts it: no key was dropped, and one that skipped items names\n * some. Always a copy, so the failure it came from may be retained and re-merged.\n */\nexport function normalizeSkippedItems(skippedItems: CollectionSkippedItems): CollectionSkippedItems {\n\tconst { keys, total, complete } = (\n\t\ttypeof skippedItems === 'object' && skippedItems != null ? skippedItems : {}\n\t) as {\n\t\tkeys?: unknown;\n\t\ttotal?: unknown;\n\t\tcomplete?: unknown;\n\t};\n\tconst strings = Array.isArray(keys) ? keys.filter((key): key is string => typeof key === 'string') : [];\n\tconst named = [...new Set(strings)];\n\tconst count = Math.max(typeof total === 'number' && Number.isFinite(total) ? total : 0, named.length);\n\treturn {\n\t\tkeys: named,\n\t\ttotal: count,\n\t\tcomplete:\n\t\t\tcomplete === true &&\n\t\t\tArray.isArray(keys) &&\n\t\t\tstrings.length === keys.length &&\n\t\t\t(named.length > 0 || count === 0),\n\t};\n}\n\n/**\n * `metadata` with each failure's `skippedItems` made well-formed ({@link normalizeSkippedItems}), where a Jira page\n * enters from the SDK: `toJiraIssuePage` for the search reads, and `getPagedResult`, the generic paged path every\n * provider's read takes (a no-op unless a failure has `skippedItems`, which only Jira's search pages set), for\n * `getIssuesForResourceForCurrentUser`. The reads that report skipped items take no other path, so the stages after it\n * mostly see a well-formed one. They still normalize what they read ({@link assessCollectionMetadata}, merging), so a\n * payload that bypassed this can't throw once a read has drained every page.\n *\n * `skippedItems` marks a failure as a skip, and one that is an object stays one even when its payload is unusable:\n * its message is the skipped issues' own error text and must never classify it. Anything that isn't an object is no\n * report of skipped items and is dropped.\n */\nexport function withValidSkippedItems(metadata: CollectionMetadata | undefined): CollectionMetadata | undefined {\n\tif (!metadata?.failures?.some(failure => failure.skippedItems !== undefined)) return metadata;\n\n\treturn {\n\t\t...metadata,\n\t\tfailures: metadata.failures.map(({ skippedItems, ...failure }): CollectionScopeFailure => {\n\t\t\tif (skippedItems == null || typeof skippedItems !== 'object') return failure;\n\n\t\t\treturn { ...failure, skippedItems: normalizeSkippedItems(skippedItems) };\n\t\t}),\n\t};\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 skipped = failure.skippedItems != null ? normalizeSkippedItems(failure.skippedItems) : undefined;\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...(skipped != null ? { unreadableItems: { keys: skipped.keys, complete: skipped.complete } } : {}),\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.skippedItems == 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\tCollectionSkippedItems,\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} from './results.js';\nimport { appendDedupedWarning, getRequestFailureCause, 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}\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\tconst cause = kind === 'provider' ? getRequestFailureCause(ex) : 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...(cause != null ? { cause: cause } : {}),\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, which is not\n\t// the page's. A failure that reports skipped items is never classified by its message.\n\tif (failure.skippedItems != 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\tcase 'unexpected-response':\n\t\t\treturn 'the provider returned an unexpected response';\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 * A failure's `skippedItems` as a well-formed one, whatever the SDK's payload held. Without an array of `keys` it names\n * none; with one it keeps the string keys, without duplicates and in order. It counts at least the keys it names, and\n * is `complete` only when it says so and nothing contradicts it: no key was dropped, and one that skipped items names\n * some. Always a copy, so the failure it came from may be retained and re-merged.\n */\nexport function normalizeSkippedItems(skippedItems: CollectionSkippedItems): CollectionSkippedItems {\n\tconst { keys, total, complete } = (\n\t\ttypeof skippedItems === 'object' && skippedItems != null ? skippedItems : {}\n\t) as {\n\t\tkeys?: unknown;\n\t\ttotal?: unknown;\n\t\tcomplete?: unknown;\n\t};\n\tconst strings = Array.isArray(keys) ? keys.filter((key): key is string => typeof key === 'string') : [];\n\tconst named = [...new Set(strings)];\n\tconst count = Math.max(typeof total === 'number' && Number.isFinite(total) ? total : 0, named.length);\n\treturn {\n\t\tkeys: named,\n\t\ttotal: count,\n\t\tcomplete:\n\t\t\tcomplete === true &&\n\t\t\tArray.isArray(keys) &&\n\t\t\tstrings.length === keys.length &&\n\t\t\t(named.length > 0 || count === 0),\n\t};\n}\n\n/**\n * `metadata` with each failure's `skippedItems` made well-formed ({@link normalizeSkippedItems}), where a Jira page\n * enters from the SDK: `toJiraIssuePage` for the search reads, and `getPagedResult`, the generic paged path every\n * provider's read takes (a no-op unless a failure has `skippedItems`, which only Jira's search pages set), for\n * `getIssuesForResourceForCurrentUser`. The reads that report skipped items take no other path, so the stages after it\n * mostly see a well-formed one. They still normalize what they read ({@link assessCollectionMetadata}, merging), so a\n * payload that bypassed this can't throw once a read has drained every page.\n *\n * `skippedItems` marks a failure as a skip, and one that is an object stays one even when its payload is unusable:\n * its message is the skipped issues' own error text and must never classify it. Anything that isn't an object is no\n * report of skipped items and is dropped.\n */\nexport function withValidSkippedItems(metadata: CollectionMetadata | undefined): CollectionMetadata | undefined {\n\tif (!metadata?.failures?.some(failure => failure.skippedItems !== undefined)) return metadata;\n\n\treturn {\n\t\t...metadata,\n\t\tfailures: metadata.failures.map(({ skippedItems, ...failure }): CollectionScopeFailure => {\n\t\t\tif (skippedItems == null || typeof skippedItems !== 'object') return failure;\n\n\t\t\treturn { ...failure, skippedItems: normalizeSkippedItems(skippedItems) };\n\t\t}),\n\t};\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 skipped = failure.skippedItems != null ? normalizeSkippedItems(failure.skippedItems) : undefined;\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...(skipped != null ? { unreadableItems: { keys: skipped.keys, complete: skipped.complete } } : {}),\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.skippedItems == 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; requestFailed: 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\trequestFailed: assessment.requestFailed,\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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"}
@@ -16,9 +16,9 @@ export type AzureWorkItemStatesScope = {
16
16
  token: string;
17
17
  isPAT: boolean;
18
18
  };
19
- export declare function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string, apiVersion?: string): string;
20
- export declare function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string, apiVersion?: string): string;
21
- export declare function getAzureWorkItemTypeStatesUrl(baseUrl: string, namespace: string, project: string, type: string, apiVersion?: string): string;
19
+ export declare function getAzureWorkItemTypesUrl(baseUrl: string, namespace: string, project: string, apiVersion: string): string;
20
+ export declare function getAzureWorkItemTypeStatesPath(namespace: string, project: string, type: string, apiVersion: string): string;
21
+ export declare function getAzureWorkItemTypeStatesUrl(baseUrl: string, namespace: string, project: string, type: string, apiVersion: string): string;
22
22
  export declare function resolveAzureWorkItemStateCategory(snapshot: WorkItemTypeStatesSnapshot, state: string): AzureWorkItemStateCategory | undefined;
23
23
  export declare function requireAzureWorkItemStateCategories(snapshot: WorkItemTypeStatesSnapshot): NonNullable<AzureWorkItemTypeStates['types'][number]['states']>;
24
24
  /**
@@ -2,13 +2,13 @@ import { isCancellationError } from "../../../../utils/cancellation.js";
2
2
  import { PromiseCache } from "../../../../utils/promiseCache.js";
3
3
  import { azureIssueStateCategory, encodeAzurePathSegment } from "./models.js";
4
4
  //#region ../plus/integrations/src/providers/azure/workItemStateCategories.ts
5
- function getAzureWorkItemTypesUrl(baseUrl, namespace, project, apiVersion = "6.0") {
5
+ function getAzureWorkItemTypesUrl(baseUrl, namespace, project, apiVersion) {
6
6
  return `${baseUrl.replace(/\/$/, "")}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=${apiVersion}`;
7
7
  }
8
- function getAzureWorkItemTypeStatesPath(namespace, project, type, apiVersion = "6.0-preview.1") {
8
+ function getAzureWorkItemTypeStatesPath(namespace, project, type, apiVersion) {
9
9
  return `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=${apiVersion}`;
10
10
  }
11
- function getAzureWorkItemTypeStatesUrl(baseUrl, namespace, project, type, apiVersion = "6.0-preview.1") {
11
+ function getAzureWorkItemTypeStatesUrl(baseUrl, namespace, project, type, apiVersion) {
12
12
  return `${baseUrl.replace(/\/$/, "")}/${getAzureWorkItemTypeStatesPath(namespace, project, type, apiVersion)}`;
13
13
  }
14
14
  function parseWorkItemTypes(body) {
@@ -1 +1 @@
1
- {"version":3,"file":"workItemStateCategories.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemStateCategories.ts"],"sourcesContent":["import type { AzureWorkItemTypeStates } from '@gitkraken/provider-apis';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { AzureWorkItemStateCategory } from './models.js';\nimport { azureIssueStateCategory, encodeAzurePathSegment } from './models.js';\n\ntype WorkItemTypeStates = readonly { name: string; category?: string }[];\ntype WorkItemTypeStatesSnapshot = {\n\tstates: WorkItemTypeStates;\n\tusesLegacyMappings: boolean;\n};\ntype WorkItemStateMappings = ReadonlyMap<string, ReadonlyMap<string, string>>;\n\nexport type AzureWorkItemStatesScope = {\n\tbaseUrl: string;\n\tnamespace: string;\n\tproject: string;\n\ttoken: string;\n\tisPAT: boolean;\n};\n\nexport function getAzureWorkItemTypesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\tapiVersion = '6.0',\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=${apiVersion}`;\n}\n\n// Server 2020 serves this endpoint only as a preview; Services accepts the same version.\nexport function getAzureWorkItemTypeStatesPath(\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n\tapiVersion = '6.0-preview.1',\n): string {\n\treturn `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=${apiVersion}`;\n}\n\nexport function getAzureWorkItemTypeStatesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n\tapiVersion = '6.0-preview.1',\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${getAzureWorkItemTypeStatesPath(namespace, project, type, apiVersion)}`;\n}\n\nfunction parseWorkItemTypes(body: unknown): string[] {\n\tconst types = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no work item types');\n\n\tconst names: string[] = [];\n\tfor (const type of types as { name?: unknown }[]) {\n\t\tif (typeof type?.name !== 'string' || !type.name.trim()) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item type');\n\t\t}\n\n\t\tnames.push(type.name);\n\t}\n\treturn [...new Set(names)];\n}\n\nfunction parseWorkItemStates(body: unknown, allowMissingCategory = false): WorkItemTypeStates {\n\tconst states = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(states) || states.length === 0) throw new Error('Azure DevOps returned no work item states');\n\n\tconst parsed: { name: string; category?: string }[] = [];\n\tfor (const state of states as { name?: unknown; category?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof state?.name !== 'string' ||\n\t\t\t!state.name.trim() ||\n\t\t\t(state.category === undefined\n\t\t\t\t? !allowMissingCategory\n\t\t\t\t: typeof state.category !== 'string' ||\n\t\t\t\t\tazureIssueStateCategory(state.category as AzureWorkItemStateCategory) == null) ||\n\t\t\tparsed.some(s => s.name === state.name && s.category !== state.category)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item state');\n\t\t}\n\n\t\tparsed.push({ name: state.name, category: state.category as string | undefined });\n\t}\n\treturn parsed;\n}\n\nexport function resolveAzureWorkItemStateCategory(\n\tsnapshot: WorkItemTypeStatesSnapshot,\n\tstate: string,\n): AzureWorkItemStateCategory | undefined {\n\tconst matched = snapshot.states.find(s => s.name === state);\n\tconst category = matched?.category as AzureWorkItemStateCategory | undefined;\n\tif ((snapshot.usesLegacyMappings || matched != null) && category == null) {\n\t\tthrow new Error(`Azure DevOps does not expose a category for work item state '${state}'`);\n\t}\n\n\treturn category;\n}\n\nexport function requireAzureWorkItemStateCategories(\n\tsnapshot: WorkItemTypeStatesSnapshot,\n): NonNullable<AzureWorkItemTypeStates['types'][number]['states']> {\n\treturn snapshot.states.map(state => {\n\t\tif (state.category == null) throw new Error('Azure DevOps does not expose a category for this work item state');\n\n\t\treturn { name: state.name, category: state.category };\n\t});\n}\n\nfunction parseWorkItemStateMappings(body: unknown): WorkItemStateMappings {\n\tconst types = (body as { workItemTypeMappedStates?: unknown } | null)?.workItemTypeMappedStates;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no workflow category mappings');\n\n\tconst result = new Map<string, Map<string, string>>();\n\tfor (const type of types as { workItemTypeName?: unknown; states?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof type?.workItemTypeName !== 'string' ||\n\t\t\t!type.workItemTypeName.trim() ||\n\t\t\ttype.states == null ||\n\t\t\ttypeof type.states !== 'object' ||\n\t\t\tArray.isArray(type.states)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned invalid workflow category mappings');\n\t\t}\n\n\t\tconst states = result.get(type.workItemTypeName) ?? new Map<string, string>();\n\t\tfor (const [name, category] of Object.entries(type.states)) {\n\t\t\tif (\n\t\t\t\t!name.trim() ||\n\t\t\t\ttypeof category !== 'string' ||\n\t\t\t\tazureIssueStateCategory(category as AzureWorkItemStateCategory) == null ||\n\t\t\t\t(states.has(name) && states.get(name) !== category)\n\t\t\t) {\n\t\t\t\tthrow new Error('Azure DevOps returned invalid workflow category mappings');\n\t\t\t}\n\n\t\t\tstates.set(name, category);\n\t\t}\n\t\tresult.set(type.workItemTypeName, states);\n\t}\n\treturn result;\n}\n\nfunction scopeKey(scope: AzureWorkItemStatesScope, type?: string): string {\n\tencodeAzurePathSegment(scope.namespace);\n\tencodeAzurePathSegment(scope.project);\n\tif (type != null) {\n\t\tencodeAzurePathSegment(type);\n\t}\n\n\treturn JSON.stringify([\n\t\tscope.baseUrl.replace(/\\/$/, ''),\n\t\tscope.namespace.toLowerCase(),\n\t\tscope.project.toLowerCase(),\n\t\ttype?.toLowerCase(),\n\t\tscope.token,\n\t\tscope.isPAT,\n\t]);\n}\n\n/**\n * Workflow metadata per project, work item type and token, kept for 5 minutes. A failed read is kept for 30 seconds,\n * so the pages and per-type reads of a listing fail fast with the same error instead of each asking a failing server\n * again; the first read after that retries.\n */\nexport class AzureWorkItemStatesCache {\n\tprivate readonly types = new PromiseCache<string, PromiseSettledResult<string[]>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\tprivate readonly states = new PromiseCache<string, PromiseSettledResult<WorkItemTypeStatesSnapshot>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tprivate readonly mappings = new PromiseCache<string, PromiseSettledResult<WorkItemStateMappings>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tclear(): void {\n\t\tthis.types.clear();\n\t\tthis.states.clear();\n\t\tthis.mappings.clear();\n\t}\n\n\tgetTypes(\n\t\tscope: AzureWorkItemStatesScope,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<string[]> {\n\t\treturn this.read(\n\t\t\tthis.types,\n\t\t\tscopeKey(scope),\n\t\t\tasync signal => parseWorkItemTypes(await read(signal)),\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tgetStates(\n\t\tscope: AzureWorkItemStatesScope,\n\t\ttype: string,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t\treadMappings?: (signal?: AbortSignal) => Promise<unknown>,\n\t): Promise<WorkItemTypeStatesSnapshot> {\n\t\treturn this.read(\n\t\t\tthis.states,\n\t\t\tscopeKey(scope, type),\n\t\t\tasync signal => {\n\t\t\t\tconst states = parseWorkItemStates(await read(signal), readMappings != null);\n\t\t\t\tif (readMappings == null || states.every(state => state.category != null)) {\n\t\t\t\t\treturn { states: states, usesLegacyMappings: false };\n\t\t\t\t}\n\n\t\t\t\tconst mappings = await this.read(\n\t\t\t\t\tthis.mappings,\n\t\t\t\t\tscopeKey(scope),\n\t\t\t\t\tasync aggregate => parseWorkItemStateMappings(await readMappings(aggregate)),\n\t\t\t\t\tsignal,\n\t\t\t\t);\n\t\t\t\tconst mapped = mappings.get(type);\n\t\t\t\treturn {\n\t\t\t\t\tstates: states.map(state => ({\n\t\t\t\t\t\tname: state.name,\n\t\t\t\t\t\tcategory: state.category ?? mapped?.get(state.name),\n\t\t\t\t\t})),\n\t\t\t\t\tusesLegacyMappings: true,\n\t\t\t\t};\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async read<T>(\n\t\tcache: PromiseCache<string, PromiseSettledResult<T>>,\n\t\tkey: string,\n\t\tload: (signal?: AbortSignal) => Promise<T>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<T> {\n\t\tconst result = await cache.getOrCreate(\n\t\t\tkey,\n\t\t\tasync (_cacheable, signal) => ({ status: 'fulfilled', value: await load(signal) }),\n\t\t\t{\n\t\t\t\tcancellation: cancellation,\n\t\t\t\t// Every caller must still see the failure; caching undefined would turn later pages into successes.\n\t\t\t\tonError: error =>\n\t\t\t\t\tisCancellationError(error) || cache.get(key) == null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: { value: { status: 'rejected', reason: error }, createTTL: 30 * 1000, suppress: true },\n\t\t\t},\n\t\t);\n\t\tif (result.status === 'rejected') throw result.reason;\n\n\t\treturn result.value;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workItemStateCategories.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/workItemStateCategories.ts"],"sourcesContent":["import type { AzureWorkItemTypeStates } from '@gitkraken/provider-apis';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { AzureWorkItemStateCategory } from './models.js';\nimport { azureIssueStateCategory, encodeAzurePathSegment } from './models.js';\n\ntype WorkItemTypeStates = readonly { name: string; category?: string }[];\ntype WorkItemTypeStatesSnapshot = {\n\tstates: WorkItemTypeStates;\n\tusesLegacyMappings: boolean;\n};\ntype WorkItemStateMappings = ReadonlyMap<string, ReadonlyMap<string, string>>;\n\nexport type AzureWorkItemStatesScope = {\n\tbaseUrl: string;\n\tnamespace: string;\n\tproject: string;\n\ttoken: string;\n\tisPAT: boolean;\n};\n\n// Older servers return a 404 page for unsupported API versions, so callers must supply the negotiated version.\nexport function getAzureWorkItemTypesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\tapiVersion: string,\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes?api-version=${apiVersion}`;\n}\n\n// Azure DevOps Server serves this endpoint only as a preview.\nexport function getAzureWorkItemTypeStatesPath(\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n\tapiVersion: string,\n): string {\n\treturn `${encodeAzurePathSegment(namespace)}/${encodeAzurePathSegment(project)}/_apis/wit/workItemTypes/${encodeAzurePathSegment(type)}/states?api-version=${apiVersion}`;\n}\n\nexport function getAzureWorkItemTypeStatesUrl(\n\tbaseUrl: string,\n\tnamespace: string,\n\tproject: string,\n\ttype: string,\n\tapiVersion: string,\n): string {\n\treturn `${baseUrl.replace(/\\/$/, '')}/${getAzureWorkItemTypeStatesPath(namespace, project, type, apiVersion)}`;\n}\n\nfunction parseWorkItemTypes(body: unknown): string[] {\n\tconst types = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no work item types');\n\n\tconst names: string[] = [];\n\tfor (const type of types as { name?: unknown }[]) {\n\t\tif (typeof type?.name !== 'string' || !type.name.trim()) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item type');\n\t\t}\n\n\t\tnames.push(type.name);\n\t}\n\treturn [...new Set(names)];\n}\n\nfunction parseWorkItemStates(body: unknown, allowMissingCategory = false): WorkItemTypeStates {\n\tconst states = (body as { value?: unknown } | null)?.value;\n\tif (!Array.isArray(states) || states.length === 0) throw new Error('Azure DevOps returned no work item states');\n\n\tconst parsed: { name: string; category?: string }[] = [];\n\tfor (const state of states as { name?: unknown; category?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof state?.name !== 'string' ||\n\t\t\t!state.name.trim() ||\n\t\t\t(state.category === undefined\n\t\t\t\t? !allowMissingCategory\n\t\t\t\t: typeof state.category !== 'string' ||\n\t\t\t\t\tazureIssueStateCategory(state.category as AzureWorkItemStateCategory) == null) ||\n\t\t\tparsed.some(s => s.name === state.name && s.category !== state.category)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned an invalid work item state');\n\t\t}\n\n\t\tparsed.push({ name: state.name, category: state.category as string | undefined });\n\t}\n\treturn parsed;\n}\n\nexport function resolveAzureWorkItemStateCategory(\n\tsnapshot: WorkItemTypeStatesSnapshot,\n\tstate: string,\n): AzureWorkItemStateCategory | undefined {\n\tconst matched = snapshot.states.find(s => s.name === state);\n\tconst category = matched?.category as AzureWorkItemStateCategory | undefined;\n\tif ((snapshot.usesLegacyMappings || matched != null) && category == null) {\n\t\tthrow new Error(`Azure DevOps does not expose a category for work item state '${state}'`);\n\t}\n\n\treturn category;\n}\n\nexport function requireAzureWorkItemStateCategories(\n\tsnapshot: WorkItemTypeStatesSnapshot,\n): NonNullable<AzureWorkItemTypeStates['types'][number]['states']> {\n\treturn snapshot.states.map(state => {\n\t\tif (state.category == null) throw new Error('Azure DevOps does not expose a category for this work item state');\n\n\t\treturn { name: state.name, category: state.category };\n\t});\n}\n\nfunction parseWorkItemStateMappings(body: unknown): WorkItemStateMappings {\n\tconst types = (body as { workItemTypeMappedStates?: unknown } | null)?.workItemTypeMappedStates;\n\tif (!Array.isArray(types)) throw new Error('Azure DevOps returned no workflow category mappings');\n\n\tconst result = new Map<string, Map<string, string>>();\n\tfor (const type of types as { workItemTypeName?: unknown; states?: unknown }[]) {\n\t\tif (\n\t\t\ttypeof type?.workItemTypeName !== 'string' ||\n\t\t\t!type.workItemTypeName.trim() ||\n\t\t\ttype.states == null ||\n\t\t\ttypeof type.states !== 'object' ||\n\t\t\tArray.isArray(type.states)\n\t\t) {\n\t\t\tthrow new Error('Azure DevOps returned invalid workflow category mappings');\n\t\t}\n\n\t\tconst states = result.get(type.workItemTypeName) ?? new Map<string, string>();\n\t\tfor (const [name, category] of Object.entries(type.states)) {\n\t\t\tif (\n\t\t\t\t!name.trim() ||\n\t\t\t\ttypeof category !== 'string' ||\n\t\t\t\tazureIssueStateCategory(category as AzureWorkItemStateCategory) == null ||\n\t\t\t\t(states.has(name) && states.get(name) !== category)\n\t\t\t) {\n\t\t\t\tthrow new Error('Azure DevOps returned invalid workflow category mappings');\n\t\t\t}\n\n\t\t\tstates.set(name, category);\n\t\t}\n\t\tresult.set(type.workItemTypeName, states);\n\t}\n\treturn result;\n}\n\nfunction scopeKey(scope: AzureWorkItemStatesScope, type?: string): string {\n\tencodeAzurePathSegment(scope.namespace);\n\tencodeAzurePathSegment(scope.project);\n\tif (type != null) {\n\t\tencodeAzurePathSegment(type);\n\t}\n\n\treturn JSON.stringify([\n\t\tscope.baseUrl.replace(/\\/$/, ''),\n\t\tscope.namespace.toLowerCase(),\n\t\tscope.project.toLowerCase(),\n\t\ttype?.toLowerCase(),\n\t\tscope.token,\n\t\tscope.isPAT,\n\t]);\n}\n\n/**\n * Workflow metadata per project, work item type and token, kept for 5 minutes. A failed read is kept for 30 seconds,\n * so the pages and per-type reads of a listing fail fast with the same error instead of each asking a failing server\n * again; the first read after that retries.\n */\nexport class AzureWorkItemStatesCache {\n\tprivate readonly types = new PromiseCache<string, PromiseSettledResult<string[]>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\tprivate readonly states = new PromiseCache<string, PromiseSettledResult<WorkItemTypeStatesSnapshot>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tprivate readonly mappings = new PromiseCache<string, PromiseSettledResult<WorkItemStateMappings>>({\n\t\tcreateTTL: 5 * 60 * 1000,\n\t\tcapacity: 256,\n\t});\n\n\tclear(): void {\n\t\tthis.types.clear();\n\t\tthis.states.clear();\n\t\tthis.mappings.clear();\n\t}\n\n\tgetTypes(\n\t\tscope: AzureWorkItemStatesScope,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<string[]> {\n\t\treturn this.read(\n\t\t\tthis.types,\n\t\t\tscopeKey(scope),\n\t\t\tasync signal => parseWorkItemTypes(await read(signal)),\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tgetStates(\n\t\tscope: AzureWorkItemStatesScope,\n\t\ttype: string,\n\t\tread: (signal?: AbortSignal) => Promise<unknown>,\n\t\tcancellation?: AbortSignal,\n\t\treadMappings?: (signal?: AbortSignal) => Promise<unknown>,\n\t): Promise<WorkItemTypeStatesSnapshot> {\n\t\treturn this.read(\n\t\t\tthis.states,\n\t\t\tscopeKey(scope, type),\n\t\t\tasync signal => {\n\t\t\t\tconst states = parseWorkItemStates(await read(signal), readMappings != null);\n\t\t\t\tif (readMappings == null || states.every(state => state.category != null)) {\n\t\t\t\t\treturn { states: states, usesLegacyMappings: false };\n\t\t\t\t}\n\n\t\t\t\tconst mappings = await this.read(\n\t\t\t\t\tthis.mappings,\n\t\t\t\t\tscopeKey(scope),\n\t\t\t\t\tasync aggregate => parseWorkItemStateMappings(await readMappings(aggregate)),\n\t\t\t\t\tsignal,\n\t\t\t\t);\n\t\t\t\tconst mapped = mappings.get(type);\n\t\t\t\treturn {\n\t\t\t\t\tstates: states.map(state => ({\n\t\t\t\t\t\tname: state.name,\n\t\t\t\t\t\tcategory: state.category ?? mapped?.get(state.name),\n\t\t\t\t\t})),\n\t\t\t\t\tusesLegacyMappings: true,\n\t\t\t\t};\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async read<T>(\n\t\tcache: PromiseCache<string, PromiseSettledResult<T>>,\n\t\tkey: string,\n\t\tload: (signal?: AbortSignal) => Promise<T>,\n\t\tcancellation?: AbortSignal,\n\t): Promise<T> {\n\t\tconst result = await cache.getOrCreate(\n\t\t\tkey,\n\t\t\tasync (_cacheable, signal) => ({ status: 'fulfilled', value: await load(signal) }),\n\t\t\t{\n\t\t\t\tcancellation: cancellation,\n\t\t\t\t// Every caller must still see the failure; caching undefined would turn later pages into successes.\n\t\t\t\tonError: error =>\n\t\t\t\t\tisCancellationError(error) || cache.get(key) == null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: { value: { status: 'rejected', reason: error }, createTTL: 30 * 1000, suppress: true },\n\t\t\t},\n\t\t);\n\t\tif (result.status === 'rejected') throw result.reason;\n\n\t\treturn result.value;\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { IssueSorting } from "../../../git/models/issue.js";
2
- import "../providerFilters.js";
3
2
  //#region ../plus/integrations/src/providers/issueSorts.d.ts
4
3
  /** GitHub and GHE: one `search` channel serves all three issue reads, so all three order the same way. */
5
4
  export declare const githubIssueSorts: IssueSorting[];
@@ -1,6 +1,6 @@
1
1
  import { equalsIgnoreCase } from "../../../utils/string.js";
2
2
  import { gitSuffixRegex } from "../../../git/utils/remote.utils.js";
3
- import { Issue } from "../../../git/models/issue.js";
3
+ import { Issue as Issue$1 } from "../../../git/models/issue.js";
4
4
  import { PullRequest, PullRequestFilter } from "../../../git/models/pullRequest.js";
5
5
  import { isTerminalIssueStateCategory } from "../../../git/utils/issue.utils.js";
6
6
  import "../constants.js";
@@ -12,8 +12,7 @@ import { azurePullRequestSearchSorts, azureWorkItemSearchRelationships, azureWor
12
12
  import { azureAccountWideIssueSorts, azureIssueSorts, githubAccountWideIssueSorts, githubIssueSorts, gitlabAccountWideIssueSorts, gitlabIssueSorts, jiraIssueSorts, linearIssueSorts, trelloIssueSorts } from "./issueSorts.js";
13
13
  import { fromPullRequestReviewDecision, toCompletedReviews, toProviderReviewDecision, toProviderReviews, toReviewRequests } from "./pullRequestReviews.js";
14
14
  import { getEntityIdentifierInput } from "./utils.js";
15
- import { EntityIdentifierUtils } from "@gitkraken/provider-apis/entity-identifiers";
16
- import { GitProviderUtils } from "@gitkraken/provider-apis/provider-utils";
15
+ import { EntityIdentifierUtils, GitProviderUtils } from "@gitkraken/provider-apis";
17
16
  //#region ../plus/integrations/src/providers/models.ts
18
17
  const GitBuildStatusState = {
19
18
  ActionRequired: "ACTION_REQUIRED",
@@ -1016,7 +1015,7 @@ function fromProviderIssue(issue, integration, options) {
1016
1015
  const identifier = toIssueIdentifier(issue.number);
1017
1016
  const state = getProviderIssueState(issue, integration.id);
1018
1017
  const viewer = toIssueViewer(issue, options?.viewer);
1019
- return new Issue(integration, identifier, issue.graphQLId ?? issue.id, issue.title, issue.url ?? "", issue.createdDate, issue.updatedDate ?? issue.closedDate ?? issue.createdDate, state === "closed", state, fromProviderAccount(issue.author), issue.assignees?.map(fromProviderAccount) ?? void 0, issue.repository != null ? {
1018
+ return new Issue$1(integration, identifier, issue.graphQLId ?? issue.id, issue.title, issue.url ?? "", issue.createdDate, issue.updatedDate ?? issue.closedDate ?? issue.createdDate, state === "closed", state, fromProviderAccount(issue.author), issue.assignees?.map(fromProviderAccount) ?? void 0, issue.repository != null ? {
1020
1019
  owner: issue.repository.owner.login ?? "",
1021
1020
  repo: issue.repository.name,
1022
1021
  id: issue.repository.id || void 0