@gitkraken/core-gitlens 0.22.0 → 0.23.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 +16 -1
- package/dist/git-cli/exec/git.js +1 -1
- package/dist/git-cli/providers/config.js +2 -2
- package/dist/git-cli/providers/pausedOperations.js +2 -2
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/staging.js +1 -1
- 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/integrations/index.d.ts +3 -3
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +10 -2
- package/dist/plus/integrations/integrationService.js +9 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +25 -1
- package/dist/plus/integrations/models/issueReads.d.ts +5 -0
- package/dist/plus/integrations/providers/azure/azure.js +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +2 -2
- package/dist/plus/integrations/providers/jira.js +2 -2
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/linear.d.ts +35 -2
- package/dist/plus/integrations/providers/linear.js +106 -9
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +4 -1
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providerApiClient.js +4 -1
- package/dist/plus/integrations/providers/providerApiClient.js.map +1 -1
- package/dist/plus/integrations/providers/providerErrors.d.ts +4 -1
- package/dist/plus/integrations/providers/providerErrors.js +13 -4
- package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +7 -1
- package/dist/plus/integrations/providers/providersApi.js +9 -1
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/reads/cursors.d.ts +8 -0
- package/dist/plus/integrations/reads/cursors.js +14 -4
- package/dist/plus/integrations/reads/cursors.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.d.ts +8 -2
- package/dist/plus/integrations/reads/hierarchy.js +42 -2
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.d.ts +10 -0
- package/dist/plus/integrations/reads/issueTracker.js +46 -5
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/selectionConfirmation.d.ts +5 -3
- package/dist/plus/integrations/reads/selectionConfirmation.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +1 -1
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +18 -2
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/plus/integrations/utils/integration.utils.d.ts +3 -0
- package/dist/plus/integrations/utils/integration.utils.js +5 -1
- package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
- package/dist/utils/env/node/exec.js +2 -2
- package/docs/integrations.md +37 -21
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providerApiClient.js","names":[],"sources":["../../../../../plus/integrations/src/providers/providerApiClient.ts"],"sourcesContent":["import ProviderApis from '@gitkraken/provider-apis';\nimport { getAuthenticatedUser } from '../errors.js';\nimport type { ProviderRequestFunction, ProviderRequestOptions, ProviderRequestResponse } from './models.js';\nimport {\n\tAnonymousResponseError,\n\tgetProviderResponseBodyMessage,\n\tmarkRequestError,\n\tUnexpectedHtmlResponseError,\n} from './providerErrors.js';\n\n// `@gitkraken/provider-apis` is published as CommonJS with its factory on the `default` export.\n// How that surfaces depends on the consuming bundler's CJS->ESM interop: esbuild yields the\n// callable factory directly, while webpack and Node surface the module namespace (with the\n// factory under `.default`). Normalize to the callable factory so every consumer resolves it.\ntype ProviderApisFactory = typeof ProviderApis;\nexport const createProviderApis: ProviderApisFactory =\n\t(ProviderApis as ProviderApisFactory & { default?: ProviderApisFactory }).default ?? ProviderApis;\n\nexport function createProviderApiRequest(\n\tfetch: (input: string | URL, init?: RequestInit) => Promise<Response>,\n\tuserAgent?: string,\n): ProviderRequestFunction {\n\treturn async <T>({ url, ...options }: ProviderRequestOptions): Promise<ProviderRequestResponse<T>> => {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\t...options,\n\t\t\t\theaders: { ...(userAgent != null ? { 'User-Agent': userAgent } : {}), ...options.headers },\n\t\t\t});\n\t\t\treturn await parseFetchResponseForApi<T>(response, () => hasAuthorization(options.headers));\n\t\t} catch (ex) {\n\t\t\tthrow markRequestError(ex);\n\t\t}\n\t};\n}\n\nexport async function parseFetchResponseForApi<T>(\n\tresponse: Response,\n\tsentCredential?: () => boolean,\n): Promise<ProviderRequestResponse<T>> {\n\t// Media types are case-insensitive (RFC 9110 §8.3.1) and `fetch` hands the header back exactly as the server\n\t// wrote it, so match on a lowercased copy rather than the raw value.\n\tconst contentType = (response.headers.get('content-type') || '').toLowerCase();\n\n\t// A credential the server served as the anonymous user was rejected, whatever the body says; reading it as data\n\t// reports an account with nothing in it (see `AnonymousResponseError`). Checked before the body is parsed, which\n\t// may itself fail. A 401 already fails as the credential's rejection, with its body, and a throttle or a server\n\t// failure keeps its own error: neither proves anything about the credential.\n\tif (\n\t\tresponse.status !== 401 &&\n\t\tresponse.status !== 429 &&\n\t\tresponse.status < 500 &&\n\t\tgetAuthenticatedUser(response.headers) === 'anonymous' &&\n\t\tsentCredential?.() === true\n\t) {\n\t\t// Released rather than left for the collector: an unread body holds its connection.\n\t\tvoid response.body?.cancel().catch(() => undefined);\n\t\tthrow new AnonymousResponseError(response.status, {\n\t\t\theaders: Object.fromEntries(response.headers.entries()),\n\t\t\tstatus: response.status,\n\t\t});\n\t}\n\n\tlet body;\n\tlet servedHtml = false;\n\n\t// parse the response body\n\tif (contentType.startsWith('application/json')) {\n\t\tconst text = await response.text();\n\t\t// A sign-in page a server mislabels as JSON is the same page under another header, and `<` can never open\n\t\t// valid JSON — so recognize it rather than letting JSON.parse throw a SyntaxError carrying no status.\n\t\tif (text.trimStart().startsWith('<')) {\n\t\t\tservedHtml = true;\n\t\t\tbody = text;\n\t\t} else {\n\t\t\tbody = text.trim().length > 0 ? JSON.parse(text) : null;\n\t\t}\n\t} else if (contentType.startsWith('text/') || contentType === '') {\n\t\tbody = await response.text();\n\t\t// An empty body is never a sign-in page, and a write answering `204` can carry a stale `text/html` from\n\t\t// whatever its endpoint usually returns — so require actual markup, not just the header.\n\t\tservedHtml = contentType.startsWith('text/html') && body.trim().length > 0;\n\t} else if (contentType.startsWith('application/vnd.github.raw+json')) {\n\t\tbody = await response.arrayBuffer();\n\t} else {\n\t\tthrow new Error(`Unsupported content-type: ${contentType}`);\n\t}\n\n\tconst result = {\n\t\tbody: body,\n\t\theaders: Object.fromEntries(response.headers.entries()),\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t};\n\n\t// A 2xx carrying a page rather than data is a rejected credential wearing a success status (GKDEV-3617); a\n\t// non-2xx keeps its own status error below, which is the better diagnostic.\n\tif (response.ok && servedHtml) {\n\t\tthrow new UnexpectedHtmlResponseError(response.status, contentType, result);\n\t}\n\n\t// throw an error if the response is not ok\n\tif (!response.ok) {\n\t\tconst status = `(${response.status})${response.statusText ? ` ${response.statusText}` : ''}.`;\n\t\tconst detail = getProviderResponseBodyMessage(body);\n\t\tconst error = new Error(detail != null ? `${status} ${detail}` : status);\n\t\tObject.assign(error, { response: result });\n\t\tthrow error;\n\t}\n\n\treturn result;\n}\n\nfunction hasAuthorization(headers: ProviderRequestOptions['headers']): boolean {\n\tif (headers == null) return false;\n\n\treturn Object.entries(headers).some(([name, value]) => name.toLowerCase() === 'authorization' && Boolean(value));\n}\n"],"mappings":";;;;AAeA,MAAa,qBACX,aAAyE,WAAW;AAEtF,SAAgB,yBACf,OACA,WAC0B;CAC1B,OAAO,OAAU,EAAE,KAAK,GAAG,cAA2E;EACrG,IAAI;GAKH,OAAO,MAAM,yBAA4B,MAJlB,MAAM,KAAK;IACjC,GAAG;IACH,SAAS;KAAE,GAAI,aAAa,OAAO,EAAE,cAAc,UAAU,IAAI,CAAC;KAAI,GAAG,QAAQ;IAAQ;GAC1F,CAAC,SACwD,iBAAiB,QAAQ,OAAO,CAAC;EAC3F,SAAS,IAAI;GACZ,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"providerApiClient.js","names":[],"sources":["../../../../../plus/integrations/src/providers/providerApiClient.ts"],"sourcesContent":["import ProviderApis from '@gitkraken/provider-apis';\nimport { getAuthenticatedUser } from '../errors.js';\nimport type { ProviderRequestFunction, ProviderRequestOptions, ProviderRequestResponse } from './models.js';\nimport {\n\tAnonymousResponseError,\n\tgetProviderResponseBodyMessage,\n\tmarkRequestError,\n\tUnexpectedHtmlResponseError,\n} from './providerErrors.js';\n\n// `@gitkraken/provider-apis` is published as CommonJS with its factory on the `default` export.\n// How that surfaces depends on the consuming bundler's CJS->ESM interop: esbuild yields the\n// callable factory directly, while webpack and Node surface the module namespace (with the\n// factory under `.default`). Normalize to the callable factory so every consumer resolves it.\ntype ProviderApisFactory = typeof ProviderApis;\nexport const createProviderApis: ProviderApisFactory =\n\t(ProviderApis as ProviderApisFactory & { default?: ProviderApisFactory }).default ?? ProviderApis;\n\nexport function createProviderApiRequest(\n\tfetch: (input: string | URL, init?: RequestInit) => Promise<Response>,\n\tuserAgent?: string,\n): ProviderRequestFunction {\n\treturn async <T>({ url, ...options }: ProviderRequestOptions): Promise<ProviderRequestResponse<T>> => {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\t...options,\n\t\t\t\theaders: { ...(userAgent != null ? { 'User-Agent': userAgent } : {}), ...options.headers },\n\t\t\t});\n\t\t\treturn await parseFetchResponseForApi<T>(response, () => hasAuthorization(options.headers));\n\t\t} catch (ex) {\n\t\t\tthrow markRequestError(ex, { url: url, method: options.method });\n\t\t}\n\t};\n}\n\nexport async function parseFetchResponseForApi<T>(\n\tresponse: Response,\n\tsentCredential?: () => boolean,\n): Promise<ProviderRequestResponse<T>> {\n\t// Media types are case-insensitive (RFC 9110 §8.3.1) and `fetch` hands the header back exactly as the server\n\t// wrote it, so match on a lowercased copy rather than the raw value.\n\tconst contentType = (response.headers.get('content-type') || '').toLowerCase();\n\n\t// A credential the server served as the anonymous user was rejected, whatever the body says; reading it as data\n\t// reports an account with nothing in it (see `AnonymousResponseError`). Checked before the body is parsed, which\n\t// may itself fail. A 401 already fails as the credential's rejection, with its body, and a throttle or a server\n\t// failure keeps its own error: neither proves anything about the credential.\n\tif (\n\t\tresponse.status !== 401 &&\n\t\tresponse.status !== 429 &&\n\t\tresponse.status < 500 &&\n\t\tgetAuthenticatedUser(response.headers) === 'anonymous' &&\n\t\tsentCredential?.() === true\n\t) {\n\t\t// Released rather than left for the collector: an unread body holds its connection.\n\t\tvoid response.body?.cancel().catch(() => undefined);\n\t\tthrow new AnonymousResponseError(response.status, {\n\t\t\theaders: Object.fromEntries(response.headers.entries()),\n\t\t\tstatus: response.status,\n\t\t});\n\t}\n\n\tlet body;\n\tlet servedHtml = false;\n\n\t// parse the response body\n\tif (contentType.startsWith('application/json')) {\n\t\tconst text = await response.text();\n\t\t// A sign-in page a server mislabels as JSON is the same page under another header, and `<` can never open\n\t\t// valid JSON — so recognize it rather than letting JSON.parse throw a SyntaxError carrying no status.\n\t\tif (text.trimStart().startsWith('<')) {\n\t\t\tservedHtml = true;\n\t\t\tbody = text;\n\t\t} else {\n\t\t\tbody = text.trim().length > 0 ? JSON.parse(text) : null;\n\t\t}\n\t} else if (contentType.startsWith('text/') || contentType === '') {\n\t\tbody = await response.text();\n\t\t// An empty body is never a sign-in page, and a write answering `204` can carry a stale `text/html` from\n\t\t// whatever its endpoint usually returns — so require actual markup, not just the header.\n\t\tservedHtml = contentType.startsWith('text/html') && body.trim().length > 0;\n\t} else if (contentType.startsWith('application/vnd.github.raw+json')) {\n\t\tbody = await response.arrayBuffer();\n\t} else {\n\t\tthrow new Error(`Unsupported content-type: ${contentType}`);\n\t}\n\n\tconst result = {\n\t\tbody: body,\n\t\theaders: Object.fromEntries(response.headers.entries()),\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t};\n\n\t// A 2xx carrying a page rather than data is a rejected credential wearing a success status (GKDEV-3617); a\n\t// non-2xx keeps its own status error below, which is the better diagnostic.\n\tif (response.ok && servedHtml) {\n\t\tthrow new UnexpectedHtmlResponseError(response.status, contentType, result);\n\t}\n\n\t// throw an error if the response is not ok\n\tif (!response.ok) {\n\t\tconst status = `(${response.status})${response.statusText ? ` ${response.statusText}` : ''}.`;\n\t\tconst detail = getProviderResponseBodyMessage(body);\n\t\tconst error = new Error(detail != null ? `${status} ${detail}` : status);\n\t\tObject.assign(error, { response: result });\n\t\tthrow error;\n\t}\n\n\treturn result;\n}\n\nfunction hasAuthorization(headers: ProviderRequestOptions['headers']): boolean {\n\tif (headers == null) return false;\n\n\treturn Object.entries(headers).some(([name, value]) => name.toLowerCase() === 'authorization' && Boolean(value));\n}\n"],"mappings":";;;;AAeA,MAAa,qBACX,aAAyE,WAAW;AAEtF,SAAgB,yBACf,OACA,WAC0B;CAC1B,OAAO,OAAU,EAAE,KAAK,GAAG,cAA2E;EACrG,IAAI;GAKH,OAAO,MAAM,yBAA4B,MAJlB,MAAM,KAAK;IACjC,GAAG;IACH,SAAS;KAAE,GAAI,aAAa,OAAO,EAAE,cAAc,UAAU,IAAI,CAAC;KAAI,GAAG,QAAQ;IAAQ;GAC1F,CAAC,SACwD,iBAAiB,QAAQ,OAAO,CAAC;EAC3F,SAAS,IAAI;GACZ,MAAM,iBAAiB,IAAI;IAAO;IAAK,QAAQ,QAAQ;GAAO,CAAC;EAChE;CACD;AACD;AAEA,eAAsB,yBACrB,UACA,gBACsC;CAGtC,MAAM,eAAe,SAAS,QAAQ,IAAI,cAAc,KAAK,GAAA,CAAI,YAAY;CAM7E,IACC,SAAS,WAAW,OACpB,SAAS,WAAW,OACpB,SAAS,SAAS,OAClB,qBAAqB,SAAS,OAAO,MAAM,eAC3C,iBAAiB,MAAM,MACtB;EAED,SAAc,MAAM,OAAO,CAAC,CAAC,YAAY,KAAA,CAAS;EAClD,MAAM,IAAI,uBAAuB,SAAS,QAAQ;GACjD,SAAS,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC;GACtD,QAAQ,SAAS;EAClB,CAAC;CACF;CAEA,IAAI;CACJ,IAAI,aAAa;CAGjB,IAAI,YAAY,WAAW,kBAAkB,GAAG;EAC/C,MAAM,OAAO,MAAM,SAAS,KAAK;EAGjC,IAAI,KAAK,UAAU,CAAC,CAAC,WAAW,GAAG,GAAG;GACrC,aAAa;GACb,OAAO;EACR,OACC,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,KAAK,MAAM,IAAI,IAAI;CAErD,OAAO,IAAI,YAAY,WAAW,OAAO,KAAK,gBAAgB,IAAI;EACjE,OAAO,MAAM,SAAS,KAAK;EAG3B,aAAa,YAAY,WAAW,WAAW,KAAK,KAAK,KAAK,CAAC,CAAC,SAAS;CAC1E,OAAO,IAAI,YAAY,WAAW,iCAAiC,GAClE,OAAO,MAAM,SAAS,YAAY;MAElC,MAAM,IAAI,MAAM,6BAA6B,aAAa;CAG3D,MAAM,SAAS;EACR;EACN,SAAS,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC;EACtD,QAAQ,SAAS;EACjB,YAAY,SAAS;CACtB;CAIA,IAAI,SAAS,MAAM,YAClB,MAAM,IAAI,4BAA4B,SAAS,QAAQ,aAAa,MAAM;CAI3E,IAAI,CAAC,SAAS,IAAI;EACjB,MAAM,SAAS,IAAI,SAAS,OAAO,GAAG,SAAS,aAAa,IAAI,SAAS,eAAe,GAAG;EAC3F,MAAM,SAAS,+BAA+B,IAAI;EAClD,MAAM,QAAQ,IAAI,MAAM,UAAU,OAAO,GAAG,OAAO,GAAG,WAAW,MAAM;EACvE,OAAO,OAAO,OAAO,EAAE,UAAU,OAAO,CAAC;EACzC,MAAM;CACP;CAEA,OAAO;AACR;AAEA,SAAS,iBAAiB,SAAqD;CAC9E,IAAI,WAAW,MAAM,OAAO;CAE5B,OAAO,OAAO,QAAQ,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,WAAW,KAAK,YAAY,MAAM,mBAAmB,QAAQ,KAAK,CAAC;AAChH"}
|
|
@@ -43,7 +43,10 @@ export declare class UnexpectedResponseError extends Error {
|
|
|
43
43
|
* {@link throwProviderError} never reads it as data a read could not handle, whatever the host's fetch throws.
|
|
44
44
|
* Returns `ex`, to rethrow.
|
|
45
45
|
*/
|
|
46
|
-
export declare function markRequestError(ex: unknown
|
|
46
|
+
export declare function markRequestError(ex: unknown, request?: {
|
|
47
|
+
url: string;
|
|
48
|
+
method?: string;
|
|
49
|
+
}): unknown;
|
|
47
50
|
/**
|
|
48
51
|
* A response that names the anonymous user (`X-AUSERNAME: anonymous`) to a request that carried a credential.
|
|
49
52
|
*
|
|
@@ -56,14 +56,17 @@ var UnexpectedResponseError = class UnexpectedResponseError extends Error {
|
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
58
|
/** What the request function `ProvidersApi` hands `provider-apis` threw itself: see {@link markRequestError}. */
|
|
59
|
-
const requestErrors = /* @__PURE__ */ new
|
|
59
|
+
const requestErrors = /* @__PURE__ */ new WeakMap();
|
|
60
60
|
/**
|
|
61
61
|
* Records an error the request function threw itself (a transport failure, or a response it refused), so
|
|
62
62
|
* {@link throwProviderError} never reads it as data a read could not handle, whatever the host's fetch throws.
|
|
63
63
|
* Returns `ex`, to rethrow.
|
|
64
64
|
*/
|
|
65
|
-
function markRequestError(ex) {
|
|
66
|
-
if (ex != null && typeof ex === "object") requestErrors.
|
|
65
|
+
function markRequestError(ex, request) {
|
|
66
|
+
if (ex != null && typeof ex === "object") requestErrors.set(ex, request != null && URL.canParse(request.url) ? {
|
|
67
|
+
path: new URL(request.url).pathname,
|
|
68
|
+
method: (request.method ?? "GET").toUpperCase()
|
|
69
|
+
} : void 0);
|
|
67
70
|
return ex;
|
|
68
71
|
}
|
|
69
72
|
/**
|
|
@@ -156,7 +159,13 @@ function isLinearRateLimitedBody(body) {
|
|
|
156
159
|
function isProviderIssueNotFoundError(providerId, ex) {
|
|
157
160
|
if (AnonymousResponseError.is(ex)) return false;
|
|
158
161
|
if (providerId === "linear") return ex instanceof Error && (linearIssueNotFoundMessage.test(ex.message) || linearIssueEntityNotFoundMessage.test(ex.message));
|
|
159
|
-
const
|
|
162
|
+
const response = ex?.response;
|
|
163
|
+
const status = response?.status;
|
|
164
|
+
if (providerId === "jira" || providerId === "jira-server") {
|
|
165
|
+
const request = ex != null && typeof ex === "object" ? requestErrors.get(ex) : void 0;
|
|
166
|
+
const messages = (response?.body)?.errorMessages;
|
|
167
|
+
return status === 404 && request?.method === "GET" && /\/rest\/api\/[23]\/issue\/[^/]+$/.test(request.path) && Array.isArray(messages) && messages.length > 0 && messages.every((message) => typeof message === "string" && message.trim().length > 0);
|
|
168
|
+
}
|
|
160
169
|
return status === 404 || status === 410 || status === 422;
|
|
161
170
|
}
|
|
162
171
|
const jiraMissingProjectMessage = /^The value '.*' does not exist for the field 'project'\.?$/i;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providerErrors.js","names":[],"sources":["../../../../../plus/integrations/src/providers/providerErrors.ts"],"sourcesContent":["import { Logger } from '@gitlens/utils/logger.js';\nimport 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\tisProviderUnreachableError,\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 provider read that failed on the data a request returned rather than on the request: `provider-apis` reads a\n * response body as the shape it expects without checking it, so a 2xx carrying anything else (a gateway's JSON\n * error, `{}`, `null`, a GraphQL answer with neither data nor errors) surfaces as a `TypeError` thrown deep inside the\n * read, such as `response.body.map is not a function` from Jira Cloud's site list or Trello's boards, or\n * `Cannot read properties of undefined (reading 'id')` from Linear's organization. See {@link throwProviderError}.\n *\n * The message is the readable one a warning carries. The `TypeError` is kept as `original`, and a log line, which\n * prints the error through `toString()`, names it too.\n */\nexport class UnexpectedResponseError extends Error {\n\tstatic is(ex: unknown): ex is UnexpectedResponseError {\n\t\treturn ex instanceof UnexpectedResponseError;\n\t}\n\n\tconstructor(readonly original: Error) {\n\t\tsuper('Expected data but the provider returned an unexpected response');\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n\n\toverride toString(): string {\n\t\treturn `${super.toString()} (${String(this.original)})`;\n\t}\n}\n\n/** What the request function `ProvidersApi` hands `provider-apis` threw itself: see {@link markRequestError}. */\nconst requestErrors = new WeakSet<object>();\n\n/**\n * Records an error the request function threw itself (a transport failure, or a response it refused), so\n * {@link throwProviderError} never reads it as data a read could not handle, whatever the host's fetch throws.\n * Returns `ex`, to rethrow.\n */\nexport function markRequestError(ex: unknown): unknown {\n\tif (ex != null && typeof ex === 'object') {\n\t\trequestErrors.add(ex);\n\t}\n\treturn ex;\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\treturn isLinearRateLimitedBody((ex as { response?: { body?: unknown } }).response?.body);\n}\n\n/** Whether a Linear GraphQL response body reports throttling. */\nexport function isLinearRateLimitedBody(body: unknown): boolean {\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\t// A `TypeError` the request function didn't throw was thrown while reading what it returned: a body that isn't the\n\t// shape the read expects (see `UnexpectedResponseError`). A transport failure that reached here some other way stays\n\t// one. Neither a session expiry nor a strike toward disconnecting, as before: nothing says the credential is at\n\t// fault, and the provider may answer properly next time. Logged here because a warning or a fan-out's scope failure\n\t// built from it carries only the readable message, and a fan-out never reaches `handleProviderException`'s log.\n\tif (error instanceof TypeError && !requestErrors.has(error) && !isProviderUnreachableError(error)) {\n\t\tconst unexpected = new UnexpectedResponseError(error);\n\t\tLogger.warn(`${tokenWithInfo.providerId}: ${String(unexpected)}`);\n\t\tthrow unexpected;\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 { Logger } from '@gitlens/utils/logger.js';\nimport type { TokenWithInfo } from '../authentication/models.js';\nimport type { IntegrationIds } from '../constants.js';\nimport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n\tIssuesSelfManagedHostIntegrationId,\n} from '../constants.js';\nimport type { ResponseHeaders } from '../errors.js';\nimport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tgetAuthenticatedUser,\n\tisProviderUnreachableError,\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 provider read that failed on the data a request returned rather than on the request: `provider-apis` reads a\n * response body as the shape it expects without checking it, so a 2xx carrying anything else (a gateway's JSON\n * error, `{}`, `null`, a GraphQL answer with neither data nor errors) surfaces as a `TypeError` thrown deep inside the\n * read, such as `response.body.map is not a function` from Jira Cloud's site list or Trello's boards, or\n * `Cannot read properties of undefined (reading 'id')` from Linear's organization. See {@link throwProviderError}.\n *\n * The message is the readable one a warning carries. The `TypeError` is kept as `original`, and a log line, which\n * prints the error through `toString()`, names it too.\n */\nexport class UnexpectedResponseError extends Error {\n\tstatic is(ex: unknown): ex is UnexpectedResponseError {\n\t\treturn ex instanceof UnexpectedResponseError;\n\t}\n\n\tconstructor(readonly original: Error) {\n\t\tsuper('Expected data but the provider returned an unexpected response');\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n\n\toverride toString(): string {\n\t\treturn `${super.toString()} (${String(this.original)})`;\n\t}\n}\n\n/** What the request function `ProvidersApi` hands `provider-apis` threw itself: see {@link markRequestError}. */\nconst requestErrors = new WeakMap<object, { path: string; method: string } | undefined>();\n\n/**\n * Records an error the request function threw itself (a transport failure, or a response it refused), so\n * {@link throwProviderError} never reads it as data a read could not handle, whatever the host's fetch throws.\n * Returns `ex`, to rethrow.\n */\nexport function markRequestError(ex: unknown, request?: { url: string; method?: string }): unknown {\n\tif (ex != null && typeof ex === 'object') {\n\t\trequestErrors.set(\n\t\t\tex,\n\t\t\trequest != null && URL.canParse(request.url)\n\t\t\t\t? { path: new URL(request.url).pathname, method: (request.method ?? 'GET').toUpperCase() }\n\t\t\t\t: undefined,\n\t\t);\n\t}\n\treturn ex;\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\treturn isLinearRateLimitedBody((ex as { response?: { body?: unknown } }).response?.body);\n}\n\n/** Whether a Linear GraphQL response body reports throttling. */\nexport function isLinearRateLimitedBody(body: unknown): boolean {\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 response = (ex as { response?: { status?: unknown; body?: unknown } } | null | undefined)?.response;\n\tconst status = response?.status;\n\tif (\n\t\tproviderId === IssuesCloudHostIntegrationId.Jira ||\n\t\tproviderId === IssuesSelfManagedHostIntegrationId.JiraServer\n\t) {\n\t\t// SDK reads discover fields and sites first; only the issue endpoint can prove an issue's absence.\n\t\tconst request = ex != null && typeof ex === 'object' ? requestErrors.get(ex) : undefined;\n\t\tconst messages = (response?.body as { errorMessages?: unknown } | null | undefined)?.errorMessages;\n\t\treturn (\n\t\t\tstatus === 404 &&\n\t\t\trequest?.method === 'GET' &&\n\t\t\t/\\/rest\\/api\\/[23]\\/issue\\/[^/]+$/.test(request.path) &&\n\t\t\tArray.isArray(messages) &&\n\t\t\tmessages.length > 0 &&\n\t\t\tmessages.every(message => typeof message === 'string' && message.trim().length > 0)\n\t\t);\n\t}\n\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\t// A `TypeError` the request function didn't throw was thrown while reading what it returned: a body that isn't the\n\t// shape the read expects (see `UnexpectedResponseError`). A transport failure that reached here some other way stays\n\t// one. Neither a session expiry nor a strike toward disconnecting, as before: nothing says the credential is at\n\t// fault, and the provider may answer properly next time. Logged here because a warning or a fan-out's scope failure\n\t// built from it carries only the readable message, and a fan-out never reaches `handleProviderException`'s log.\n\tif (error instanceof TypeError && !requestErrors.has(error) && !isProviderUnreachableError(error)) {\n\t\tconst unexpected = new UnexpectedResponseError(error);\n\t\tLogger.warn(`${tokenWithInfo.providerId}: ${String(unexpected)}`);\n\t\tthrow unexpected;\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":";;;;;AAsBA,MAAM,6BAA6B;;;;;;;;;;;;;;;;;AAkBnC,IAAa,8BAAb,MAAa,oCAAoC,MAAM;CACtD,OAAO,GAAG,IAAgD;EACzD,OAAO,cAAc;CACtB;CAEA;CAEA,YAAY,QAAgB,aAAqB,UAA6C;EAC7F,MAAM,IAAI,OAAO,wEAAwE,YAAY,EAAE;EAEvG,KAAK,WAAW,sBAAsB,QAAQ,QAAQ;EAEtD,MAAM,oBAAoB,MAAM,UAAU;CAC3C;AACD;;;;;;;;;;;AAYA,IAAa,0BAAb,MAAa,gCAAgC,MAAM;CAK7B;CAJrB,OAAO,GAAG,IAA4C;EACrD,OAAO,cAAc;CACtB;CAEA,YAAY,UAA0B;EACrC,MAAM,gEAAgE;EADlD,KAAA,WAAA;EAGpB,MAAM,oBAAoB,MAAM,UAAU;CAC3C;CAEA,WAA4B;EAC3B,OAAO,GAAG,MAAM,SAAS,EAAE,IAAI,OAAO,KAAK,QAAQ,EAAE;CACtD;AACD;;AAGA,MAAM,gCAAgB,IAAI,QAA8D;;;;;;AAOxF,SAAgB,iBAAiB,IAAa,SAAqD;CAClG,IAAI,MAAM,QAAQ,OAAO,OAAO,UAC/B,cAAc,IACb,IACA,WAAW,QAAQ,IAAI,SAAS,QAAQ,GAAG,IACxC;EAAE,MAAM,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC;EAAU,SAAS,QAAQ,UAAU,MAAA,CAAO,YAAY;CAAE,IACvF,KAAA,CACJ;CAED,OAAO;AACR;;;;;;;;;;;;;AAcA,IAAa,yBAAb,MAAa,+BAA+B,MAAM;CACjD,OAAO,GAAG,IAA2C;EACpD,OAAO,cAAc;CACtB;CAEA;CAEA,YAAY,QAAgB,UAA6C;EACxE,MAAM,IAAI,OAAO,6EAA6E;EAE9F,KAAK,WAAW,sBAAsB,QAAQ,QAAQ;EAEtD,MAAM,oBAAoB,MAAM,UAAU;CAC3C;AACD;;;;;;AAUA,MAAM,4CAA4B,IAAI,IAAI;CACzC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;AAGD,SAAS,sBAAsB,QAAgB,UAAqE;CACnH,MAAM,UAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,UAAU,WAAW,CAAC,CAAC,GAAG;EACpE,IAAI,0BAA0B,IAAI,KAAK,YAAY,CAAC,GAAG;EAEvD,QAAQ,QAAQ;CACjB;CAEA,MAAM,OAAO,UAAU;CACvB,OAAO;EAEN,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,0BAA0B,EAAE,IAAI,CAAC;EAC7E;EACT,QAAQ,UAAU,UAAU;CAC7B;AACD;AAEA,MAAM,6BAA6B;;;;;;;AAQnC,MAAM,mCAAmC;AAOzC,SAAS,yBAAyB,MAAuC;CACxE,IAAI,QAAQ,QAAQ,OAAO,SAAS,UAAU,OAAO,CAAC;CAEtD,MAAM,SAAU,KAA8B;CAC9C,OAAO,MAAM,QAAQ,MAAM,IACxB,OAAO,QAAQ,UAAyC,SAAS,QAAQ,OAAO,UAAU,QAAQ,IAClG,CAAC;AACL;AAEA,SAAgB,kBACf,YAC0G;CAC1G,OACC,eAAA,iBACA,eAAA;AAEF;AAEA,SAAgB,mBACf,YACyG;CACzG,OACC,eAAA,YACA,eAAA;AAEF;;;;;;AAOA,MAAa,wBAAwB;AAErC,SAAS,uBAAuB,YAA4B,IAAsB;CACjF,IAAI,eAAA,UAAoD,OAAO;CAE/D,OAAO,wBAAyB,GAAyC,UAAU,IAAI;AACxF;;AAGA,SAAgB,wBAAwB,MAAwB;CAC/D,OAAO,yBAAyB,IAAI,CAAC,CAAC,MAAK,UAAS,MAAM,YAAY,SAAS,aAAa;AAC7F;AAEA,SAAgB,6BAA6B,YAA4B,IAAsB;CAE9F,IAAI,uBAAuB,GAAG,EAAE,GAAG,OAAO;CAE1C,IAAI,eAAA,UACH,OACC,cAAc,UACb,2BAA2B,KAAK,GAAG,OAAO,KAAK,iCAAiC,KAAK,GAAG,OAAO;CAIlG,MAAM,WAAY,IAA+E;CACjG,MAAM,SAAS,UAAU;CACzB,IACC,eAAA,UACA,eAAA,eACC;EAED,MAAM,UAAU,MAAM,QAAQ,OAAO,OAAO,WAAW,cAAc,IAAI,EAAE,IAAI,KAAA;EAC/E,MAAM,YAAY,UAAU,KAAA,EAAyD;EACrF,OACC,WAAW,OACX,SAAS,WAAW,SACpB,mCAAmC,KAAK,QAAQ,IAAI,KACpD,MAAM,QAAQ,QAAQ,KACtB,SAAS,SAAS,KAClB,SAAS,OAAM,YAAW,OAAO,YAAY,YAAY,QAAQ,KAAK,CAAC,CAAC,SAAS,CAAC;CAEpF;CAEA,OAAO,WAAW,OAAO,WAAW,OAAO,WAAW;AACvD;AAEA,MAAM,4BAA4B;;;;;;;;;;;AAYlC,SAAgB,0BAA0B,IAAsB;CAC/D,OAAO,8BAA8B,EAAE,KAAK;AAC7C;;;;;;;AAQA,SAAgB,gCAAgC,IAAsB;CACrE,MAAM,OAAO,qBAAqB,8BAA8B,EAAE,CAAC,EAAE,OAAO;CAC5E,OAAO,QAAQ,QAAQ,SAAS;AACjC;AAEA,SAAS,8BAA8B,IAAwD;CAC9F,KAAK,MAAM,aAAa,CAAC,IAAK,IAA2C,QAAQ,GAAG;EACnF,MAAM,WACL,WACE;EACH,IAAI,UAAU,WAAW,KAAK;EAE9B,MAAM,WAAY,SAAS,MAAyD;EACpF,IAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,WAAW,GAAG;EAEvD,OAAO,SAAS,OAAM,MAAK,OAAO,MAAM,YAAY,0BAA0B,KAAK,EAAE,KAAK,CAAC,CAAC,IACzF,WACA,KAAA;CACJ;AAGD;AAEA,SAAgB,mBAAmB,eAA8B,OAAuB;CACvF,MAAM,EAAE,aAAa,OAAO,GAAG,cAAc;CAC7C,MAAM,aAAa,cAAc;CACjC,MAAM,kBAAkB,QAAQ,KAAK;CAOrC,IAAI,4BAA4B,GAAG,KAAK,GACvC,MAAM,kBAAkB,UAAU,IAC/B,IAAI,oBAAoB,WAAA,GAAmD,eAAe,IAC1F,IAAI,mBAAmB,eAAe;CAG1C,IAAI,uBAAuB,GAAG,KAAK,GAClC,MAAM,IAAI,oBAAoB,WAAA,GAAmD,eAAe;CAIjG,IAAI,uBAAuB,YAAY,KAAK,GAC3C,MAAM,iBAAiB,iBAAiB,KAAK;CAG9C,MAAM,SAAU,MAA8C,UAAU;CACxE,IAAI,OAAO,WAAW,UACrB,QAAQ,QAAR;EACC,KAAK;EACL,KAAK;EACL,KAAK,KACJ,MAAM,IAAI,qBAAqB,eAAe;EAC/C,KAAK,KACJ,MAAM,iBAAiB,iBAAiB,KAAK;EAC9C,KAAK;EACL,KAAK;GAEJ,IAAI,oBAAoB;IAAU;IAAQ,SAAS,gBAAgB;GAAQ,CAAC,GAC3E,MAAM,iBAAiB,iBAAiB,KAAK;GAE9C,MAAM,IAAI,oBACT,WACA,WAAW,MAAA,IAAA,GACX,eACD;EACD,SACC,IAAI,UAAU,OAAO,SAAS,KAC7B,MAAM,IAAI,mBAAmB,eAAe;CAE/C;CAQD,IAAI,iBAAiB,aAAa,CAAC,cAAc,IAAI,KAAK,KAAK,CAAC,2BAA2B,KAAK,GAAG;EAClG,MAAM,aAAa,IAAI,wBAAwB,KAAK;EACpD,QAAO,KAAK,GAAG,cAAc,WAAW,IAAI,OAAO,UAAU,GAAG;EAChE,MAAM;CACP;CAEA,MAAM;AACP;;AAGA,SAAgB,+BAA+B,MAAmC;CACjF,IAAI;CACJ,IAAI,OAAO,SAAS,UACnB,UAAU;MACJ,IAAI,QAAQ,QAAQ,OAAO,SAAS,UAAU;EACpD,MAAM,EAAE,SAAS,QAAQ,UAAU;EACnC,IAAI,OAAO,WAAW,UACrB,UAAU;OACJ,IAAI,OAAO,UAAU,UAC3B,UAAU;OACJ,IAAI,SAAS,QAAQ,OAAO,UAAU,UAAU;GACtD,MAAM,SAAU,MAAgC;GAChD,IAAI,OAAO,WAAW,UACrB,UAAU;EAEZ;EACA,IAAI,WAAW,MAAM;GACpB,MAAM,eAAe,yBAAyB,IAAI,CAAC,CAAC;GACpD,IAAI,OAAO,cAAc,YAAY,UACpC,UAAU,aAAa;QACjB,IAAI,OAAO,cAAc,YAAY,SAAS,UACpD,UAAU,aAAa,WAAW;EAEpC;CACD;CAEA,UAAU,SAAS,KAAK;CACxB,OAAO,UAAU,QAAQ,MAAM,GAAG,0BAA0B,IAAI,KAAA;AACjE"}
|
|
@@ -7,7 +7,7 @@ import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId, Integration
|
|
|
7
7
|
import { ProviderOrganization } from "../results.js";
|
|
8
8
|
import { ProviderPullRequestCount, ProviderPullRequestSearchPage } from "../models/pullRequestReads.js";
|
|
9
9
|
import { IssueFilter } from "../providerFilters.js";
|
|
10
|
-
import { GetIssuesOptions, GetPullRequestsForUserOptions, GetPullRequestsOptions, GetReposOptions, JiraIssuePage, PageInfo, PagingInput, PagingMode, ProviderAccount, ProviderApiCollectionResult, ProviderApiPagedResult, ProviderAzureProject, ProviderAzureResource, ProviderBitbucketResource, ProviderGitHubOrganization, ProviderGitLabGroup, ProviderHierarchyResult, ProviderIssue, ProviderJiraProject, ProviderJiraResource, ProviderJiraServerProject, ProviderLinearOrganization, ProviderLinearTeam, ProviderPullRequest, ProviderRepoInput, ProviderReposInput, ProviderRepository } from "./models.js";
|
|
10
|
+
import { GetIssuesOptions, GetPullRequestsForUserOptions, GetPullRequestsOptions, GetReposOptions, JiraIssuePage, PageInfo, PagingInput, PagingMode, ProviderAccount, ProviderApiCollectionResult, ProviderApiPagedResult, ProviderAzureProject, ProviderAzureResource, ProviderBitbucketResource, ProviderGitHubOrganization, ProviderGitLabGroup, ProviderHierarchyResult, ProviderIssue, ProviderJiraProject, ProviderJiraResource, ProviderJiraServerProject, ProviderLinearOrganization, ProviderLinearProject, ProviderLinearTeam, ProviderPullRequest, ProviderRepoInput, ProviderReposInput, ProviderRepository } from "./models.js";
|
|
11
11
|
import { AzurePullRequest, AzureWorkItemEtagRead } from "./azure/models.js";
|
|
12
12
|
import { AzureWorkItemStatesCache } from "./azure/workItemStateCategories.js";
|
|
13
13
|
import { AzureWorkItemDescriptionsCache } from "./azure/workItemDescriptions.js";
|
|
@@ -171,6 +171,12 @@ export declare class ProvidersApi implements Disposable {
|
|
|
171
171
|
getJiraResourcesForCurrentUser(tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Jira>): Promise<ProviderJiraResource[] | undefined>;
|
|
172
172
|
getLinearOrganization(tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Linear>): Promise<ProviderLinearOrganization | undefined>;
|
|
173
173
|
getLinearTeamsForCurrentUser(tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Linear>): Promise<ProviderLinearTeam[] | undefined>;
|
|
174
|
+
/**
|
|
175
|
+
* One page of the workspace's Linear Projects (Linear's `Project` entity, not its teams). Follow `paging.cursor`.
|
|
176
|
+
*/
|
|
177
|
+
getLinearProjects(tokenOptInfo: TokenWithInfo<IssuesCloudHostIntegrationId.Linear>, options?: {
|
|
178
|
+
cursor?: string;
|
|
179
|
+
}): Promise<ProviderApiPagedResult<ProviderLinearProject>>;
|
|
174
180
|
/**
|
|
175
181
|
* Reads issues scoped to Linear teams/projects/labels/assignees (Linear's issue-list filter). One page per
|
|
176
182
|
* call — follow `paging.cursor`. `assignees` takes Linear user ids (the viewer's `id`), not names.
|
|
@@ -271,6 +271,7 @@ var ProvidersApi = class {
|
|
|
271
271
|
getLinearOrganizationFn: providerApis.linear.getLinearOrganization.bind(providerApis.linear),
|
|
272
272
|
getLinearTeamsForCurrentUserFn: providerApis.linear.getTeamsForCurrentUser.bind(providerApis.linear),
|
|
273
273
|
getLinearIssuesFn: providerApis.linear.getIssues.bind(providerApis.linear),
|
|
274
|
+
getLinearProjectsFn: providerApis.linear.getProjects.bind(providerApis.linear),
|
|
274
275
|
getLinearCurrentUserFn: providerApis.linear.getCurrentUser.bind(providerApis.linear)
|
|
275
276
|
},
|
|
276
277
|
["trello"]: {
|
|
@@ -810,6 +811,13 @@ var ProvidersApi = class {
|
|
|
810
811
|
}
|
|
811
812
|
}
|
|
812
813
|
/**
|
|
814
|
+
* One page of the workspace's Linear Projects (Linear's `Project` entity, not its teams). Follow `paging.cursor`.
|
|
815
|
+
*/
|
|
816
|
+
async getLinearProjects(tokenOptInfo, options) {
|
|
817
|
+
const { provider, tokenWithInfo } = await this.ensureProviderTokenAndFunction(tokenOptInfo, "getLinearProjectsFn");
|
|
818
|
+
return this.getPagedResult({}, provider.getLinearProjectsFn, tokenWithInfo, options?.cursor);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
813
821
|
* Reads issues scoped to Linear teams/projects/labels/assignees (Linear's issue-list filter). One page per
|
|
814
822
|
* call — follow `paging.cursor`. `assignees` takes Linear user ids (the viewer's `id`), not names.
|
|
815
823
|
*/
|
|
@@ -1534,7 +1542,7 @@ var ProvidersApi = class {
|
|
|
1534
1542
|
try {
|
|
1535
1543
|
return await requestJiraIssueByKey(this.request, tokenWithInfo.accessToken, resourceId, resourceUrl, key);
|
|
1536
1544
|
} catch (e) {
|
|
1537
|
-
if (
|
|
1545
|
+
if (isProviderIssueNotFoundError(tokenWithInfo.providerId, e)) return void 0;
|
|
1538
1546
|
return this.handleProviderError(tokenWithInfo, e);
|
|
1539
1547
|
}
|
|
1540
1548
|
}
|