@gitkraken/core-gitlens 0.13.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.
- package/CHANGELOG.md +43 -2
- package/dist/git/features.d.ts +1 -1
- package/dist/git/features.js +1 -0
- package/dist/git/features.js.map +1 -1
- package/dist/git/models/branch.d.ts +5 -0
- package/dist/git/models/branch.js +1 -1
- package/dist/git/models/branch.js.map +1 -1
- package/dist/git/models/reference.d.ts +3 -0
- package/dist/git/models/status.js +1 -1
- package/dist/git/parsers/diffParser.js +1 -1
- package/dist/git/providers/operations.d.ts +3 -2
- package/dist/git/providers/provider.d.ts +15 -4
- package/dist/git/providers/revision.d.ts +17 -3
- package/dist/git/run.types.d.ts +12 -0
- package/dist/git/utils/branch.utils.d.ts +4 -1
- package/dist/git/utils/branch.utils.js +20 -5
- package/dist/git/utils/branch.utils.js.map +1 -1
- package/dist/git/utils/reference.utils.d.ts +1 -0
- package/dist/git/utils/reference.utils.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +8 -2
- package/dist/git-cli/cliGitProvider.js +75 -13
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +5 -0
- package/dist/git-cli/exec/git.js +28 -5
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/indexLock.d.ts +3 -2
- package/dist/git-cli/exec/indexLock.js +16 -6
- package/dist/git-cli/exec/indexLock.js.map +1 -1
- package/dist/git-cli/parsers/blameParser.js +2 -2
- package/dist/git-cli/parsers/refParser.js +4 -2
- package/dist/git-cli/parsers/refParser.js.map +1 -1
- package/dist/git-cli/parsers/statusParser.d.ts +6 -0
- package/dist/git-cli/parsers/statusParser.js +33 -3
- package/dist/git-cli/parsers/statusParser.js.map +1 -1
- package/dist/git-cli/providers/branchRemote.d.ts +14 -0
- package/dist/git-cli/providers/branchRemote.js +36 -0
- package/dist/git-cli/providers/branchRemote.js.map +1 -0
- package/dist/git-cli/providers/branches.js +6 -3
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.js +2 -2
- package/dist/git-cli/providers/config.js +2 -2
- package/dist/git-cli/providers/contributors.js +1 -1
- package/dist/git-cli/providers/graph.js +9 -6
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/operations.js +20 -16
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.js +2 -2
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/revision.d.ts +6 -2
- package/dist/git-cli/providers/revision.js +38 -12
- package/dist/git-cli/providers/revision.js.map +1 -1
- package/dist/git-cli/providers/staging.js +1 -1
- package/dist/git-cli/providers/status.js +2 -2
- package/dist/ipc/discovery.js +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -1
- package/dist/plus/git-github/providers/github/revision.d.ts +6 -2
- package/dist/plus/git-github/providers/github/revision.js +15 -10
- package/dist/plus/git-github/providers/github/revision.js.map +1 -1
- package/dist/plus/git-github/providers/github/status.js +1 -1
- package/dist/plus/integrations/collectionMetadata.d.ts +7 -0
- package/dist/plus/integrations/collectionMetadata.js +10 -1
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/errors.d.ts +5 -0
- package/dist/plus/integrations/errors.js +10 -1
- package/dist/plus/integrations/errors.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +2 -2
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +6 -2
- package/dist/plus/integrations/models/gitHostIntegration.js +37 -7
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +1 -1
- package/dist/plus/integrations/models/issuesIntegration.d.ts +33 -3
- package/dist/plus/integrations/models/issuesIntegration.js +76 -5
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.js +1 -1
- package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +5 -0
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js +5 -0
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +9 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.js +17 -9
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server.js +3 -2
- package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira-server.d.ts +3 -0
- package/dist/plus/integrations/providers/jira-server.js +15 -7
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +8 -0
- package/dist/plus/integrations/providers/jira.js +88 -58
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/linear.d.ts +4 -0
- package/dist/plus/integrations/providers/linear.js +23 -2
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/providerErrors.d.ts +24 -0
- package/dist/plus/integrations/providers/providerErrors.js +41 -4
- package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +1 -1
- package/dist/plus/integrations/providers/providersApi.js +15 -4
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/trello.d.ts +1 -0
- package/dist/plus/integrations/providers/trello.js +3 -0
- package/dist/plus/integrations/providers/trello.js.map +1 -1
- package/dist/plus/integrations/reads/cursors.js +23 -5
- package/dist/plus/integrations/reads/cursors.js.map +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +16 -0
- package/dist/plus/integrations/reads/drains.js +19 -1
- package/dist/plus/integrations/reads/drains.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +5 -3
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.utils.d.ts +3 -2
- package/dist/plus/integrations/reads/hierarchy.utils.js +4 -7
- package/dist/plus/integrations/reads/hierarchy.utils.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +71 -7
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/selectionConfirmation.d.ts +69 -0
- package/dist/plus/integrations/reads/selectionConfirmation.js +79 -0
- package/dist/plus/integrations/reads/selectionConfirmation.js.map +1 -0
- package/dist/plus/integrations/reads/warnings.d.ts +9 -3
- package/dist/plus/integrations/reads/warnings.js +19 -3
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +30 -3
- package/dist/plus/integrations/results.js +2 -1
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/utils/env/node/exec.js +2 -2
- package/docs/integrations.md +59 -6
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AuthenticationError, RequestClientError, RequestNotFoundError } from "../../../git/errors.js";
|
|
2
|
-
import { isRateLimitResponse, toError, toRateLimitError } from "../errors.js";
|
|
2
|
+
import { getAuthenticatedUser, isRateLimitResponse, toError, toRateLimitError } from "../errors.js";
|
|
3
3
|
import "../constants.js";
|
|
4
4
|
//#region ../plus/integrations/src/providers/providerErrors.ts
|
|
5
5
|
const maxProviderErrorBodyLength = 500;
|
|
@@ -31,6 +31,29 @@ var UnexpectedHtmlResponseError = class UnexpectedHtmlResponseError extends Erro
|
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
33
|
/**
|
|
34
|
+
* A response that names the anonymous user (`X-AUSERNAME: anonymous`) to a request that carried a credential.
|
|
35
|
+
*
|
|
36
|
+
* Jira Data Center serves a token it rejects as the anonymous user rather than refusing it: measured against Jira
|
|
37
|
+
* Data Center 10.7.3 with a revoked token, `/rest/api/2/project` answers `200 []` and a project search answers the
|
|
38
|
+
* same `400` it gives a deleted project. Read as data, that is an account with no projects, and a selected project
|
|
39
|
+
* looks deleted. The server only names `anonymous` when the credential did not authenticate, so the response is the
|
|
40
|
+
* credential's rejection whatever its status, except a 401, which already fails as one, and a 429 or a 5xx, which
|
|
41
|
+
* prove nothing about the credential.
|
|
42
|
+
*
|
|
43
|
+
* Its response keeps only the status and headers, stripped like {@link UnexpectedHtmlResponseError}'s.
|
|
44
|
+
*/
|
|
45
|
+
var AnonymousResponseError = class AnonymousResponseError extends Error {
|
|
46
|
+
static is(ex) {
|
|
47
|
+
return ex instanceof AnonymousResponseError;
|
|
48
|
+
}
|
|
49
|
+
response;
|
|
50
|
+
constructor(status, response) {
|
|
51
|
+
super(`(${status}) The provider served a request sent with a credential as the anonymous user`);
|
|
52
|
+
this.response = sanitizeErrorResponse(status, response);
|
|
53
|
+
Error.captureStackTrace?.(this, new.target);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
34
57
|
* Response headers a rejected-credential page carries that must not ride along on an error: the session cookie it
|
|
35
58
|
* sets, the challenges whose base64 payload decodes to the tenant or the internal domain, and the request-side
|
|
36
59
|
* names a substituted fetch can echo back onto a response.
|
|
@@ -92,6 +115,7 @@ function isLinearRateLimitError(providerId, ex) {
|
|
|
92
115
|
return getProviderGraphQLErrors(body).some((error) => error.extensions?.code === "RATELIMITED");
|
|
93
116
|
}
|
|
94
117
|
function isProviderIssueNotFoundError(providerId, ex) {
|
|
118
|
+
if (AnonymousResponseError.is(ex)) return false;
|
|
95
119
|
if (providerId === "linear") return ex instanceof Error && (linearIssueNotFoundMessage.test(ex.message) || linearIssueEntityNotFoundMessage.test(ex.message));
|
|
96
120
|
const status = ex.response?.status;
|
|
97
121
|
return status === 404 || status === 410 || status === 422;
|
|
@@ -108,20 +132,33 @@ const jiraMissingProjectMessage = /^The value '.*' does not exist for the field
|
|
|
108
132
|
* `throwProviderError` wraps it in, whose `original` keeps the response.
|
|
109
133
|
*/
|
|
110
134
|
function isJiraMissingProjectError(ex) {
|
|
135
|
+
return getJiraMissingProjectResponse(ex) != null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* {@link isJiraMissingProjectError} for Jira Data Center, which serves a credential it rejects as the anonymous user:
|
|
139
|
+
* the search then names a project the anonymous user cannot see and is refused with the very same `400`, measured
|
|
140
|
+
* against Jira Data Center 10.7.3 with a revoked token. Only the user it names in `X-AUSERNAME` tells the two apart,
|
|
141
|
+
* so a refusal that names no user, or `anonymous`, is a failure rather than a project that is gone.
|
|
142
|
+
*/
|
|
143
|
+
function isJiraServerMissingProjectError(ex) {
|
|
144
|
+
const user = getAuthenticatedUser(getJiraMissingProjectResponse(ex)?.headers);
|
|
145
|
+
return user != null && user !== "anonymous";
|
|
146
|
+
}
|
|
147
|
+
function getJiraMissingProjectResponse(ex) {
|
|
111
148
|
for (const candidate of [ex, ex?.original]) {
|
|
112
149
|
const response = candidate?.response;
|
|
113
150
|
if (response?.status !== 400) continue;
|
|
114
151
|
const messages = response.body?.errorMessages;
|
|
115
152
|
if (!Array.isArray(messages) || messages.length === 0) continue;
|
|
116
|
-
return messages.every((m) => typeof m === "string" && jiraMissingProjectMessage.test(m.trim()));
|
|
153
|
+
return messages.every((m) => typeof m === "string" && jiraMissingProjectMessage.test(m.trim())) ? response : void 0;
|
|
117
154
|
}
|
|
118
|
-
return false;
|
|
119
155
|
}
|
|
120
156
|
function throwProviderError(tokenWithInfo, error) {
|
|
121
157
|
const { accessToken: token, ...tokenInfo } = tokenWithInfo;
|
|
122
158
|
const providerId = tokenWithInfo.providerId;
|
|
123
159
|
const normalizedError = toError(error);
|
|
124
160
|
if (UnexpectedHtmlResponseError.is(error)) throw isAzureProviderId(providerId) ? new AuthenticationError(tokenInfo, 2, normalizedError) : new RequestClientError(normalizedError);
|
|
161
|
+
if (AnonymousResponseError.is(error)) throw new AuthenticationError(tokenInfo, 2, normalizedError);
|
|
125
162
|
if (isLinearRateLimitError(providerId, error)) throw toRateLimitError(normalizedError, token);
|
|
126
163
|
const status = error.response?.status;
|
|
127
164
|
if (typeof status === "number") switch (status) {
|
|
@@ -162,6 +199,6 @@ function getProviderResponseBodyMessage(body) {
|
|
|
162
199
|
return message ? message.slice(0, maxProviderErrorBodyLength) : void 0;
|
|
163
200
|
}
|
|
164
201
|
//#endregion
|
|
165
|
-
export { UnexpectedHtmlResponseError, getProviderResponseBodyMessage, githubGraphQLInt32Max, isAzureProviderId, isGitHubProviderId, isJiraMissingProjectError, isProviderIssueNotFoundError, throwProviderError };
|
|
202
|
+
export { AnonymousResponseError, UnexpectedHtmlResponseError, getProviderResponseBodyMessage, githubGraphQLInt32Max, isAzureProviderId, isGitHubProviderId, isJiraMissingProjectError, isJiraServerMissingProjectError, isProviderIssueNotFoundError, throwProviderError };
|
|
166
203
|
|
|
167
204
|
//# sourceMappingURL=providerErrors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providerErrors.js","names":[],"sources":["../../../../../plus/integrations/src/providers/providerErrors.ts"],"sourcesContent":["import type { TokenWithInfo } from '../authentication/models.js';\nimport type { IntegrationIds } from '../constants.js';\nimport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n} from '../constants.js';\nimport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tisRateLimitResponse,\n\tRequestClientError,\n\tRequestNotFoundError,\n\ttoError,\n\ttoRateLimitError,\n} from '../errors.js';\n\nconst maxProviderErrorBodyLength = 500;\n\n/**\n * A 2xx response carrying an HTML page instead of data.\n *\n * Azure DevOps answers a rejected credential by redirecting to its sign-in page, which then returns\n * `203 text/html` rather than `401`. `response.ok` spans the whole 200-299 range, so that page used to be read as\n * a successful body and handed to provider code typed as the JSON it is not — surfacing as an opaque `TypeError`\n * deep inside a provider, or as nothing at all on a route that returns `void`, which made a bad credential look\n * like a successful write (GKDEV-3617).\n *\n * `provider-apis` 0.59.0 raises its own error for this, but only from the transport it wraps; this package passes\n * its own already-parsed request function, so the check has to live here too.\n *\n * The message carries no URL, and the attached response is stripped of its credential-bearing headers and cut to\n * a diagnostic prefix: an error is something a consumer may reasonably log or forward, and the response it is\n * built from is a sign-in page carrying a session cookie.\n */\nexport class UnexpectedHtmlResponseError extends Error {\n\tstatic is(ex: unknown): ex is UnexpectedHtmlResponseError {\n\t\treturn ex instanceof UnexpectedHtmlResponseError;\n\t}\n\n\treadonly response: SanitizedErrorResponse;\n\n\tconstructor(status: number, contentType: string, response: ProviderErrorResponse | undefined) {\n\t\tsuper(`(${status}) Expected data but the provider returned an HTML page (content-type: ${contentType})`);\n\n\t\tthis.response = sanitizeErrorResponse(status, response);\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\ntype ProviderErrorResponse = { body?: unknown; headers?: Record<string, string>; status?: number };\ntype SanitizedErrorResponse = { body?: string; headers: Record<string, string>; status: number };\n\n/**\n * Response headers a rejected-credential page carries that must not ride along on an error: the session cookie it\n * sets, the challenges whose base64 payload decodes to the tenant or the internal domain, and the request-side\n * names a substituted fetch can echo back onto a response.\n */\nconst unloggableResponseHeaders = new Set([\n\t'authentication-info',\n\t'authorization',\n\t'cookie',\n\t'proxy-authenticate',\n\t'proxy-authentication-info',\n\t'proxy-authorization',\n\t'set-cookie',\n\t'www-authenticate',\n\t'x-api-key',\n\t'x-auth-token',\n]);\n\n/** Strips the credential-bearing headers from a response and cuts its body to a diagnostic prefix. */\nfunction sanitizeErrorResponse(status: number, response: ProviderErrorResponse | undefined): SanitizedErrorResponse {\n\tconst headers: Record<string, string> = {};\n\tfor (const [name, value] of Object.entries(response?.headers ?? {})) {\n\t\tif (unloggableResponseHeaders.has(name.toLowerCase())) continue;\n\n\t\theaders[name] = value;\n\t}\n\n\tconst body = response?.body;\n\treturn {\n\t\t// A sign-in page runs to tens of kilobytes and none of it is a diagnostic; keep only what identifies it.\n\t\t...(typeof body === 'string' ? { body: body.slice(0, maxProviderErrorBodyLength) } : {}),\n\t\theaders: headers,\n\t\tstatus: response?.status ?? status,\n\t};\n}\n\nconst linearIssueNotFoundMessage = /^Linear issue not found: .+$/i;\n\n/**\n * What Linear actually answers for an identifier it can't resolve: HTTP 200 with the single GraphQL error\n * `Entity not found: Issue` (`INPUT_ERROR`), which provider-apis rethrows as `Linear GraphQL errors: <messages>`, the\n * response not attached. Only that one message proves the miss; any other error, or another alongside it, is a\n * failure.\n */\nconst linearIssueEntityNotFoundMessage = /^Linear GraphQL errors: Entity not found: Issue$/;\n\ntype ProviderGraphQLError = {\n\tmessage?: unknown;\n\textensions?: { code?: unknown };\n};\n\nfunction getProviderGraphQLErrors(body: unknown): ProviderGraphQLError[] {\n\tif (body == null || typeof body !== 'object') return [];\n\n\tconst errors = (body as { errors?: unknown }).errors;\n\treturn Array.isArray(errors)\n\t\t? errors.filter((error): error is ProviderGraphQLError => error != null && typeof error === 'object')\n\t\t: [];\n}\n\nexport function isAzureProviderId(\n\tproviderId: IntegrationIds,\n): providerId is GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer {\n\treturn (\n\t\tproviderId === GitCloudHostIntegrationId.AzureDevOps ||\n\t\tproviderId === GitSelfManagedHostIntegrationId.AzureDevOpsServer\n\t);\n}\n\nexport function isGitHubProviderId(\n\tproviderId: IntegrationIds,\n): providerId is GitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise {\n\treturn (\n\t\tproviderId === GitCloudHostIntegrationId.GitHub ||\n\t\tproviderId === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t);\n}\n\n/**\n * GitHub's GraphQL schema declares coordinate variables (issue/PR number) as `Int!`, a 32-bit signed integer.\n * A number above this coerces with no `path` on the error, which fails every aliased target in the chunk rather\n * than just the one with the bad number — so the read layer refuses the whole call up front instead.\n */\nexport const githubGraphQLInt32Max = 2147483647;\n\nfunction isLinearRateLimitError(providerId: IntegrationIds, ex: unknown): boolean {\n\tif (providerId !== IssuesCloudHostIntegrationId.Linear) return false;\n\n\tconst body = (ex as { response?: { body?: unknown } }).response?.body;\n\treturn getProviderGraphQLErrors(body).some(error => error.extensions?.code === 'RATELIMITED');\n}\n\nexport function isProviderIssueNotFoundError(providerId: IntegrationIds, ex: unknown): boolean {\n\tif (providerId === IssuesCloudHostIntegrationId.Linear) {\n\t\treturn (\n\t\t\tex instanceof Error &&\n\t\t\t(linearIssueNotFoundMessage.test(ex.message) || linearIssueEntityNotFoundMessage.test(ex.message))\n\t\t);\n\t}\n\n\tconst status = (ex as { response?: { status?: unknown } }).response?.status;\n\treturn status === 404 || status === 410 || status === 422;\n}\n\nconst jiraMissingProjectMessage = /^The value '.*' does not exist for the field 'project'\\.?$/i;\n\n/**\n * Jira's refusal of a search scoped to a project it cannot find: `400` with\n * `{\"errorMessages\":[\"The value 'KPSC290' does not exist for the field 'project'.\"]}`, measured against Jira Data\n * Center 10.7.3 for a deleted project key and for a numeric id that names no project. Jira answers a project the\n * user lost browse permission on the same way, since it will not confirm a project the user cannot see.\n *\n * Only a `400` whose EVERY message is that one: a JQL that is wrong in some other way also answers `400`, and that\n * is a real failure rather than a project that is simply gone. Accepts the SDK's error or the `RequestClientError`\n * `throwProviderError` wraps it in, whose `original` keeps the response.\n */\nexport function isJiraMissingProjectError(ex: unknown): boolean {\n\tfor (const candidate of [ex, (ex as { original?: unknown } | undefined)?.original]) {\n\t\tconst response = (candidate as { response?: { status?: unknown; body?: unknown } } | undefined)?.response;\n\t\tif (response?.status !== 400) continue;\n\n\t\tconst messages = (response.body as { errorMessages?: unknown } | null | undefined)?.errorMessages;\n\t\tif (!Array.isArray(messages) || messages.length === 0) continue;\n\n\t\treturn messages.every(m => typeof m === 'string' && jiraMissingProjectMessage.test(m.trim()));\n\t}\n\n\treturn false;\n}\n\nexport function throwProviderError(tokenWithInfo: TokenWithInfo, error: unknown): never {\n\tconst { accessToken: token, ...tokenInfo } = tokenWithInfo;\n\tconst providerId = tokenWithInfo.providerId;\n\tconst normalizedError = toError(error);\n\t// A page served with a 2xx is only known to mean a rejected credential on Azure DevOps, which answers one by\n\t// redirecting to its sign-in page (GKDEV-3617). Elsewhere it is far more likely a maintenance or WAF page in\n\t// front of a provider, and calling that an authentication failure is not free: `handleProviderException`\n\t// expires a valid cloud session on one, and counts it against the budget that disconnects the integration.\n\t// So Azure gets the specific diagnosis and every other provider gets the honest one — the response was not\n\t// data, which is a protocol failure.\n\tif (UnexpectedHtmlResponseError.is(error)) {\n\t\tthrow isAzureProviderId(providerId)\n\t\t\t? new AuthenticationError(tokenInfo, AuthenticationErrorReason.Unauthorized, normalizedError)\n\t\t\t: new RequestClientError(normalizedError);\n\t}\n\n\t// Linear reports GraphQL throttling as HTTP 400, so its structured code must win over generic 4xx handling.\n\tif (isLinearRateLimitError(providerId, error)) {\n\t\tthrow toRateLimitError(normalizedError, token);\n\t}\n\n\tconst status = (error as { response?: { status?: unknown } }).response?.status;\n\tif (typeof status === 'number') {\n\t\tswitch (status) {\n\t\t\tcase 404:\n\t\t\tcase 410:\n\t\t\tcase 422:\n\t\t\t\tthrow new RequestNotFoundError(normalizedError);\n\t\t\tcase 429:\n\t\t\t\tthrow toRateLimitError(normalizedError, token);\n\t\t\tcase 401:\n\t\t\tcase 403:\n\t\t\t\t// Some hosts overload 403 for throttling; classify that before asking the user to reconnect.\n\t\t\t\tif (isRateLimitResponse({ status: status, message: normalizedError.message })) {\n\t\t\t\t\tthrow toRateLimitError(normalizedError, token);\n\t\t\t\t}\n\t\t\t\tthrow new AuthenticationError(\n\t\t\t\t\ttokenInfo,\n\t\t\t\t\tstatus === 401 ? AuthenticationErrorReason.Unauthorized : AuthenticationErrorReason.Forbidden,\n\t\t\t\t\tnormalizedError,\n\t\t\t\t);\n\t\t\tdefault:\n\t\t\t\tif (status >= 400 && status < 500) {\n\t\t\t\t\tthrow new RequestClientError(normalizedError);\n\t\t\t\t}\n\t\t}\n\t}\n\n\tthrow error;\n}\n\n/** Extracts the useful provider prose from an already-parsed SDK response body. */\nexport function getProviderResponseBodyMessage(body: unknown): string | undefined {\n\tlet message: string | undefined;\n\tif (typeof body === 'string') {\n\t\tmessage = body;\n\t} else if (body != null && typeof body === 'object') {\n\t\tconst { message: direct, error } = body as { message?: unknown; error?: unknown };\n\t\tif (typeof direct === 'string') {\n\t\t\tmessage = direct;\n\t\t} else if (typeof error === 'string') {\n\t\t\tmessage = error;\n\t\t} else if (error != null && typeof error === 'object') {\n\t\t\tconst nested = (error as { message?: unknown }).message;\n\t\t\tif (typeof nested === 'string') {\n\t\t\t\tmessage = nested;\n\t\t\t}\n\t\t}\n\t\tif (message == null) {\n\t\t\tconst graphQLError = getProviderGraphQLErrors(body)[0];\n\t\t\tif (typeof graphQLError?.message === 'string') {\n\t\t\t\tmessage = graphQLError.message;\n\t\t\t} else if (typeof graphQLError?.extensions?.code === 'string') {\n\t\t\t\tmessage = graphQLError.extensions.code;\n\t\t\t}\n\t\t}\n\t}\n\n\tmessage = message?.trim();\n\treturn message ? message.slice(0, maxProviderErrorBodyLength) : undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"providerErrors.js","names":[],"sources":["../../../../../plus/integrations/src/providers/providerErrors.ts"],"sourcesContent":["import type { TokenWithInfo } from '../authentication/models.js';\nimport type { IntegrationIds } from '../constants.js';\nimport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n} from '../constants.js';\nimport type { ResponseHeaders } from '../errors.js';\nimport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tgetAuthenticatedUser,\n\tisRateLimitResponse,\n\tRequestClientError,\n\tRequestNotFoundError,\n\ttoError,\n\ttoRateLimitError,\n} from '../errors.js';\n\nconst maxProviderErrorBodyLength = 500;\n\n/**\n * A 2xx response carrying an HTML page instead of data.\n *\n * Azure DevOps answers a rejected credential by redirecting to its sign-in page, which then returns\n * `203 text/html` rather than `401`. `response.ok` spans the whole 200-299 range, so that page used to be read as\n * a successful body and handed to provider code typed as the JSON it is not — surfacing as an opaque `TypeError`\n * deep inside a provider, or as nothing at all on a route that returns `void`, which made a bad credential look\n * like a successful write (GKDEV-3617).\n *\n * `provider-apis` 0.59.0 raises its own error for this, but only from the transport it wraps; this package passes\n * its own already-parsed request function, so the check has to live here too.\n *\n * The message carries no URL, and the attached response is stripped of its credential-bearing headers and cut to\n * a diagnostic prefix: an error is something a consumer may reasonably log or forward, and the response it is\n * built from is a sign-in page carrying a session cookie.\n */\nexport class UnexpectedHtmlResponseError extends Error {\n\tstatic is(ex: unknown): ex is UnexpectedHtmlResponseError {\n\t\treturn ex instanceof UnexpectedHtmlResponseError;\n\t}\n\n\treadonly response: SanitizedErrorResponse;\n\n\tconstructor(status: number, contentType: string, response: ProviderErrorResponse | undefined) {\n\t\tsuper(`(${status}) Expected data but the provider returned an HTML page (content-type: ${contentType})`);\n\n\t\tthis.response = sanitizeErrorResponse(status, response);\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\n/**\n * A response that names the anonymous user (`X-AUSERNAME: anonymous`) to a request that carried a credential.\n *\n * Jira Data Center serves a token it rejects as the anonymous user rather than refusing it: measured against Jira\n * Data Center 10.7.3 with a revoked token, `/rest/api/2/project` answers `200 []` and a project search answers the\n * same `400` it gives a deleted project. Read as data, that is an account with no projects, and a selected project\n * looks deleted. The server only names `anonymous` when the credential did not authenticate, so the response is the\n * credential's rejection whatever its status, except a 401, which already fails as one, and a 429 or a 5xx, which\n * prove nothing about the credential.\n *\n * Its response keeps only the status and headers, stripped like {@link UnexpectedHtmlResponseError}'s.\n */\nexport class AnonymousResponseError extends Error {\n\tstatic is(ex: unknown): ex is AnonymousResponseError {\n\t\treturn ex instanceof AnonymousResponseError;\n\t}\n\n\treadonly response: SanitizedErrorResponse;\n\n\tconstructor(status: number, response: ProviderErrorResponse | undefined) {\n\t\tsuper(`(${status}) The provider served a request sent with a credential as the anonymous user`);\n\n\t\tthis.response = sanitizeErrorResponse(status, response);\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\ntype ProviderErrorResponse = { body?: unknown; headers?: Record<string, string>; status?: number };\ntype SanitizedErrorResponse = { body?: string; headers: Record<string, string>; status: number };\n\n/**\n * Response headers a rejected-credential page carries that must not ride along on an error: the session cookie it\n * sets, the challenges whose base64 payload decodes to the tenant or the internal domain, and the request-side\n * names a substituted fetch can echo back onto a response.\n */\nconst unloggableResponseHeaders = new Set([\n\t'authentication-info',\n\t'authorization',\n\t'cookie',\n\t'proxy-authenticate',\n\t'proxy-authentication-info',\n\t'proxy-authorization',\n\t'set-cookie',\n\t'www-authenticate',\n\t'x-api-key',\n\t'x-auth-token',\n]);\n\n/** Strips the credential-bearing headers from a response and cuts its body to a diagnostic prefix. */\nfunction sanitizeErrorResponse(status: number, response: ProviderErrorResponse | undefined): SanitizedErrorResponse {\n\tconst headers: Record<string, string> = {};\n\tfor (const [name, value] of Object.entries(response?.headers ?? {})) {\n\t\tif (unloggableResponseHeaders.has(name.toLowerCase())) continue;\n\n\t\theaders[name] = value;\n\t}\n\n\tconst body = response?.body;\n\treturn {\n\t\t// A sign-in page runs to tens of kilobytes and none of it is a diagnostic; keep only what identifies it.\n\t\t...(typeof body === 'string' ? { body: body.slice(0, maxProviderErrorBodyLength) } : {}),\n\t\theaders: headers,\n\t\tstatus: response?.status ?? status,\n\t};\n}\n\nconst linearIssueNotFoundMessage = /^Linear issue not found: .+$/i;\n\n/**\n * What Linear actually answers for an identifier it can't resolve: HTTP 200 with the single GraphQL error\n * `Entity not found: Issue` (`INPUT_ERROR`), which provider-apis rethrows as `Linear GraphQL errors: <messages>`, the\n * response not attached. Only that one message proves the miss; any other error, or another alongside it, is a\n * failure.\n */\nconst linearIssueEntityNotFoundMessage = /^Linear GraphQL errors: Entity not found: Issue$/;\n\ntype ProviderGraphQLError = {\n\tmessage?: unknown;\n\textensions?: { code?: unknown };\n};\n\nfunction getProviderGraphQLErrors(body: unknown): ProviderGraphQLError[] {\n\tif (body == null || typeof body !== 'object') return [];\n\n\tconst errors = (body as { errors?: unknown }).errors;\n\treturn Array.isArray(errors)\n\t\t? errors.filter((error): error is ProviderGraphQLError => error != null && typeof error === 'object')\n\t\t: [];\n}\n\nexport function isAzureProviderId(\n\tproviderId: IntegrationIds,\n): providerId is GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer {\n\treturn (\n\t\tproviderId === GitCloudHostIntegrationId.AzureDevOps ||\n\t\tproviderId === GitSelfManagedHostIntegrationId.AzureDevOpsServer\n\t);\n}\n\nexport function isGitHubProviderId(\n\tproviderId: IntegrationIds,\n): providerId is GitCloudHostIntegrationId.GitHub | GitSelfManagedHostIntegrationId.CloudGitHubEnterprise {\n\treturn (\n\t\tproviderId === GitCloudHostIntegrationId.GitHub ||\n\t\tproviderId === GitSelfManagedHostIntegrationId.CloudGitHubEnterprise\n\t);\n}\n\n/**\n * GitHub's GraphQL schema declares coordinate variables (issue/PR number) as `Int!`, a 32-bit signed integer.\n * A number above this coerces with no `path` on the error, which fails every aliased target in the chunk rather\n * than just the one with the bad number — so the read layer refuses the whole call up front instead.\n */\nexport const githubGraphQLInt32Max = 2147483647;\n\nfunction isLinearRateLimitError(providerId: IntegrationIds, ex: unknown): boolean {\n\tif (providerId !== IssuesCloudHostIntegrationId.Linear) return false;\n\n\tconst body = (ex as { response?: { body?: unknown } }).response?.body;\n\treturn getProviderGraphQLErrors(body).some(error => error.extensions?.code === 'RATELIMITED');\n}\n\nexport function isProviderIssueNotFoundError(providerId: IntegrationIds, ex: unknown): boolean {\n\t// A rejected credential is not a missing issue, whatever status the server served it with.\n\tif (AnonymousResponseError.is(ex)) return false;\n\n\tif (providerId === IssuesCloudHostIntegrationId.Linear) {\n\t\treturn (\n\t\t\tex instanceof Error &&\n\t\t\t(linearIssueNotFoundMessage.test(ex.message) || linearIssueEntityNotFoundMessage.test(ex.message))\n\t\t);\n\t}\n\n\tconst status = (ex as { response?: { status?: unknown } }).response?.status;\n\treturn status === 404 || status === 410 || status === 422;\n}\n\nconst jiraMissingProjectMessage = /^The value '.*' does not exist for the field 'project'\\.?$/i;\n\n/**\n * Jira's refusal of a search scoped to a project it cannot find: `400` with\n * `{\"errorMessages\":[\"The value 'KPSC290' does not exist for the field 'project'.\"]}`, measured against Jira Data\n * Center 10.7.3 for a deleted project key and for a numeric id that names no project. Jira answers a project the\n * user lost browse permission on the same way, since it will not confirm a project the user cannot see.\n *\n * Only a `400` whose EVERY message is that one: a JQL that is wrong in some other way also answers `400`, and that\n * is a real failure rather than a project that is simply gone. Accepts the SDK's error or the `RequestClientError`\n * `throwProviderError` wraps it in, whose `original` keeps the response.\n */\nexport function isJiraMissingProjectError(ex: unknown): boolean {\n\treturn getJiraMissingProjectResponse(ex) != null;\n}\n\n/**\n * {@link isJiraMissingProjectError} for Jira Data Center, which serves a credential it rejects as the anonymous user:\n * the search then names a project the anonymous user cannot see and is refused with the very same `400`, measured\n * against Jira Data Center 10.7.3 with a revoked token. Only the user it names in `X-AUSERNAME` tells the two apart,\n * so a refusal that names no user, or `anonymous`, is a failure rather than a project that is gone.\n */\nexport function isJiraServerMissingProjectError(ex: unknown): boolean {\n\tconst user = getAuthenticatedUser(getJiraMissingProjectResponse(ex)?.headers);\n\treturn user != null && user !== 'anonymous';\n}\n\nfunction getJiraMissingProjectResponse(ex: unknown): { headers?: ResponseHeaders } | undefined {\n\tfor (const candidate of [ex, (ex as { original?: unknown } | undefined)?.original]) {\n\t\tconst response = (\n\t\t\tcandidate as { response?: { status?: unknown; body?: unknown; headers?: ResponseHeaders } } | undefined\n\t\t)?.response;\n\t\tif (response?.status !== 400) continue;\n\n\t\tconst messages = (response.body as { errorMessages?: unknown } | null | undefined)?.errorMessages;\n\t\tif (!Array.isArray(messages) || messages.length === 0) continue;\n\n\t\treturn messages.every(m => typeof m === 'string' && jiraMissingProjectMessage.test(m.trim()))\n\t\t\t? response\n\t\t\t: undefined;\n\t}\n\n\treturn undefined;\n}\n\nexport function throwProviderError(tokenWithInfo: TokenWithInfo, error: unknown): never {\n\tconst { accessToken: token, ...tokenInfo } = tokenWithInfo;\n\tconst providerId = tokenWithInfo.providerId;\n\tconst normalizedError = toError(error);\n\t// A page served with a 2xx is only known to mean a rejected credential on Azure DevOps, which answers one by\n\t// redirecting to its sign-in page (GKDEV-3617). Elsewhere it is far more likely a maintenance or WAF page in\n\t// front of a provider, and calling that an authentication failure is not free: `handleProviderException`\n\t// expires a valid cloud session on one, and counts it against the budget that disconnects the integration.\n\t// So Azure gets the specific diagnosis and every other provider gets the honest one — the response was not\n\t// data, which is a protocol failure.\n\tif (UnexpectedHtmlResponseError.is(error)) {\n\t\tthrow isAzureProviderId(providerId)\n\t\t\t? new AuthenticationError(tokenInfo, AuthenticationErrorReason.Unauthorized, normalizedError)\n\t\t\t: new RequestClientError(normalizedError);\n\t}\n\n\tif (AnonymousResponseError.is(error)) {\n\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Unauthorized, normalizedError);\n\t}\n\n\t// Linear reports GraphQL throttling as HTTP 400, so its structured code must win over generic 4xx handling.\n\tif (isLinearRateLimitError(providerId, error)) {\n\t\tthrow toRateLimitError(normalizedError, token);\n\t}\n\n\tconst status = (error as { response?: { status?: unknown } }).response?.status;\n\tif (typeof status === 'number') {\n\t\tswitch (status) {\n\t\t\tcase 404:\n\t\t\tcase 410:\n\t\t\tcase 422:\n\t\t\t\tthrow new RequestNotFoundError(normalizedError);\n\t\t\tcase 429:\n\t\t\t\tthrow toRateLimitError(normalizedError, token);\n\t\t\tcase 401:\n\t\t\tcase 403:\n\t\t\t\t// Some hosts overload 403 for throttling; classify that before asking the user to reconnect.\n\t\t\t\tif (isRateLimitResponse({ status: status, message: normalizedError.message })) {\n\t\t\t\t\tthrow toRateLimitError(normalizedError, token);\n\t\t\t\t}\n\t\t\t\tthrow new AuthenticationError(\n\t\t\t\t\ttokenInfo,\n\t\t\t\t\tstatus === 401 ? AuthenticationErrorReason.Unauthorized : AuthenticationErrorReason.Forbidden,\n\t\t\t\t\tnormalizedError,\n\t\t\t\t);\n\t\t\tdefault:\n\t\t\t\tif (status >= 400 && status < 500) {\n\t\t\t\t\tthrow new RequestClientError(normalizedError);\n\t\t\t\t}\n\t\t}\n\t}\n\n\tthrow error;\n}\n\n/** Extracts the useful provider prose from an already-parsed SDK response body. */\nexport function getProviderResponseBodyMessage(body: unknown): string | undefined {\n\tlet message: string | undefined;\n\tif (typeof body === 'string') {\n\t\tmessage = body;\n\t} else if (body != null && typeof body === 'object') {\n\t\tconst { message: direct, error } = body as { message?: unknown; error?: unknown };\n\t\tif (typeof direct === 'string') {\n\t\t\tmessage = direct;\n\t\t} else if (typeof error === 'string') {\n\t\t\tmessage = error;\n\t\t} else if (error != null && typeof error === 'object') {\n\t\t\tconst nested = (error as { message?: unknown }).message;\n\t\t\tif (typeof nested === 'string') {\n\t\t\t\tmessage = nested;\n\t\t\t}\n\t\t}\n\t\tif (message == null) {\n\t\t\tconst graphQLError = getProviderGraphQLErrors(body)[0];\n\t\t\tif (typeof graphQLError?.message === 'string') {\n\t\t\t\tmessage = graphQLError.message;\n\t\t\t} else if (typeof graphQLError?.extensions?.code === 'string') {\n\t\t\t\tmessage = graphQLError.extensions.code;\n\t\t\t}\n\t\t}\n\t}\n\n\tmessage = message?.trim();\n\treturn message ? message.slice(0, maxProviderErrorBodyLength) : undefined;\n}\n"],"mappings":"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"}
|
|
@@ -445,6 +445,6 @@ export declare class ProvidersApi {
|
|
|
445
445
|
baseUrl?: string;
|
|
446
446
|
}): Promise<ProviderIssue | undefined>;
|
|
447
447
|
}
|
|
448
|
-
export declare function parseFetchResponseForApi<T>(response: Response): Promise<ProviderRequestResponse<T>>;
|
|
448
|
+
export declare function parseFetchResponseForApi<T>(response: Response, sentCredential?: () => boolean): Promise<ProviderRequestResponse<T>>;
|
|
449
449
|
//#endregion
|
|
450
450
|
//# sourceMappingURL=providersApi.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { mapSettledBounded } from "../../../utils/promise.js";
|
|
2
2
|
import { RequestNotFoundError } from "../../../git/errors.js";
|
|
3
3
|
import { base64 } from "../../../utils/base64.js";
|
|
4
|
-
import { toError } from "../errors.js";
|
|
4
|
+
import { getAuthenticatedUser, toError } from "../errors.js";
|
|
5
5
|
import "../constants.js";
|
|
6
|
-
import { UnexpectedHtmlResponseError, getProviderResponseBodyMessage, isAzureProviderId, isProviderIssueNotFoundError, throwProviderError } from "./providerErrors.js";
|
|
6
|
+
import { AnonymousResponseError, UnexpectedHtmlResponseError, getProviderResponseBodyMessage, isAzureProviderId, isProviderIssueNotFoundError, throwProviderError } from "./providerErrors.js";
|
|
7
7
|
import { isIncompleteCollection, toCollectionScopeFailure } from "../collectionMetadata.js";
|
|
8
8
|
import { toTokenWithInfo } from "../authentication/models.js";
|
|
9
9
|
import { collectProviderPagedResult, mergeCollectionMetadata, parsePageCursor, toPageCursor } from "./utils/providerPaging.js";
|
|
@@ -135,7 +135,7 @@ var ProvidersApi = class {
|
|
|
135
135
|
"User-Agent": userAgent,
|
|
136
136
|
...options.headers
|
|
137
137
|
}
|
|
138
|
-
}));
|
|
138
|
+
}), () => hasAuthorization(options.headers));
|
|
139
139
|
};
|
|
140
140
|
this.request = customFetch;
|
|
141
141
|
const providerApis = createProviderApis({ request: customFetch });
|
|
@@ -1484,8 +1484,15 @@ var ProvidersApi = class {
|
|
|
1484
1484
|
}
|
|
1485
1485
|
}
|
|
1486
1486
|
};
|
|
1487
|
-
async function parseFetchResponseForApi(response) {
|
|
1487
|
+
async function parseFetchResponseForApi(response, sentCredential) {
|
|
1488
1488
|
const contentType = (response.headers.get("content-type") || "").toLowerCase();
|
|
1489
|
+
if (response.status !== 401 && response.status !== 429 && response.status < 500 && getAuthenticatedUser(response.headers) === "anonymous" && sentCredential?.() === true) {
|
|
1490
|
+
response.body?.cancel().catch(() => void 0);
|
|
1491
|
+
throw new AnonymousResponseError(response.status, {
|
|
1492
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
1493
|
+
status: response.status
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1489
1496
|
let body;
|
|
1490
1497
|
let servedHtml = false;
|
|
1491
1498
|
if (contentType.startsWith("application/json")) {
|
|
@@ -1515,6 +1522,10 @@ async function parseFetchResponseForApi(response) {
|
|
|
1515
1522
|
}
|
|
1516
1523
|
return result;
|
|
1517
1524
|
}
|
|
1525
|
+
function hasAuthorization(headers) {
|
|
1526
|
+
if (headers == null) return false;
|
|
1527
|
+
return Object.entries(headers).some(([name, value]) => name.toLowerCase() === "authorization" && Boolean(value));
|
|
1528
|
+
}
|
|
1518
1529
|
//#endregion
|
|
1519
1530
|
export { ProvidersApi, parseFetchResponseForApi };
|
|
1520
1531
|
|