@gitkraken/core-gitlens 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -2
- package/dist/git/remotes/azure-devops.js +3 -3
- package/dist/git/remotes/azure-devops.js.map +1 -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/authentication/configuredIntegrationService.js +9 -2
- package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +1 -1
- package/dist/plus/integrations/index.d.ts +2 -2
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +34 -0
- package/dist/plus/integrations/integrationService.js +90 -43
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/lite.js +5 -4
- package/dist/plus/integrations/lite.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +68 -23
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +30 -14
- package/dist/plus/integrations/models/gitHostIntegration.js +59 -21
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +3 -3
- package/dist/plus/integrations/models/integration.js +1 -1
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +17 -0
- package/dist/plus/integrations/models/pullRequestReads.d.ts +26 -0
- package/dist/plus/integrations/providers/azure/azure.d.ts +66 -1
- package/dist/plus/integrations/providers/azure/azure.js +192 -12
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/search.d.ts +144 -0
- package/dist/plus/integrations/providers/azure/search.js +250 -0
- package/dist/plus/integrations/providers/azure/search.js.map +1 -0
- package/dist/plus/integrations/providers/azureDevOps.d.ts +54 -35
- package/dist/plus/integrations/providers/azureDevOps.js +154 -121
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +176 -0
- package/dist/plus/integrations/providers/azureDevOpsServer.js +560 -0
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.d.ts +12 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.js +128 -0
- package/dist/plus/integrations/providers/bitbucket-server/discovery.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +10 -1
- package/dist/plus/integrations/providers/bitbucket-server/models.js +3 -2
- package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +58 -0
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +485 -0
- package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -0
- package/dist/plus/integrations/providers/bitbucket-server.d.ts +52 -3
- package/dist/plus/integrations/providers/bitbucket-server.js +96 -20
- package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket.js +1 -1
- package/dist/plus/integrations/providers/github.d.ts +5 -5
- package/dist/plus/integrations/providers/github.js +34 -33
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts +5 -3
- package/dist/plus/integrations/providers/gitlab.js +31 -27
- package/dist/plus/integrations/providers/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
- package/dist/plus/integrations/providers/jira-server.d.ts +9 -0
- package/dist/plus/integrations/providers/jira-server.js +13 -2
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.js +1 -1
- package/dist/plus/integrations/providers/linear.js +1 -1
- package/dist/plus/integrations/providers/models.d.ts +9 -0
- package/dist/plus/integrations/providers/models.js +86 -0
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +10 -0
- package/dist/plus/integrations/providers/providersApi.js +16 -1
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/utils.d.ts +12 -1
- package/dist/plus/integrations/providers/utils.js +36 -6
- package/dist/plus/integrations/providers/utils.js.map +1 -1
- package/dist/plus/integrations/reads/broaden.js +5 -5
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/counts.d.ts +18 -4
- package/dist/plus/integrations/reads/counts.js +66 -15
- package/dist/plus/integrations/reads/counts.js.map +1 -1
- package/dist/plus/integrations/reads/cursors.js +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +1 -0
- package/dist/plus/integrations/reads/filters.d.ts +1 -1
- package/dist/plus/integrations/reads/filters.js +17 -12
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +1 -1
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +1 -1
- package/dist/plus/integrations/reads/issues.js +1 -1
- package/dist/plus/integrations/reads/paging.js +1 -1
- package/dist/plus/integrations/reads/resolveRepository.d.ts +12 -11
- package/dist/plus/integrations/reads/resolveRepository.js +109 -18
- package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
- package/dist/plus/integrations/reads/searchIssues.js +4 -2
- package/dist/plus/integrations/reads/searchIssues.js.map +1 -1
- package/dist/plus/integrations/reads/searchPullRequests.js +9 -3
- package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/trackerIssue.d.ts +9 -5
- package/dist/plus/integrations/reads/trackerIssue.js +15 -9
- package/dist/plus/integrations/reads/trackerIssue.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +6 -2
- package/dist/plus/integrations/reads/warnings.js +15 -19
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/utils/domain.utils.d.ts +19 -0
- package/dist/plus/integrations/utils/domain.utils.js +36 -1
- package/dist/plus/integrations/utils/domain.utils.js.map +1 -1
- package/dist/plus/integrations/utils/integration.utils.d.ts +1 -0
- package/dist/plus/integrations/utils/integration.utils.js +12 -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 +176 -73
- package/docs/kepler-read-api-parity.md +19 -6
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trackerIssue.js","names":[],"sources":["../../../../../plus/integrations/src/reads/trackerIssue.ts"],"sourcesContent":["import type { IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IntegrationIds } from '../constants.js';\nimport { IssuesCloudHostIntegrationId } from '../constants.js';\nimport { isIssuesIntegration } from '../models/issuesIntegration.js';\nimport type { ProviderResult, ProviderWarning } from '../results.js';\nimport { isIssuesHostIntegrationId } from '../utils/integration.utils.js';\nimport type { ProviderReadContext } from './context.js';\nimport { runCaptured } from './drains.js';\nimport { issueTrackerOnlySurfaceWarning, otherWarning } from './warnings.js';\n\nexport interface TrackerIssueResult {\n\tkey: string;\n\t/**\n\t * The resolved issue, or `undefined` when it provably does not exist or is not visible to this connection.\n\t * A failed read returns no item and sets `fetchFailed`.\n\t */\n\tissue?: IssueShape;\n}\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"trackerIssue.js","names":[],"sources":["../../../../../plus/integrations/src/reads/trackerIssue.ts"],"sourcesContent":["import type { IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IntegrationIds } from '../constants.js';\nimport { IssuesCloudHostIntegrationId } from '../constants.js';\nimport { isIssuesIntegration } from '../models/issuesIntegration.js';\nimport type { ProviderResult, ProviderWarning } from '../results.js';\nimport { areDomainsOnSameHost, hostFromDomain } from '../utils/domain.utils.js';\nimport { isIssuesHostIntegrationId, isIssuesSelfManagedHostIntegrationId } from '../utils/integration.utils.js';\nimport type { ProviderReadContext } from './context.js';\nimport { runCaptured } from './drains.js';\nimport { issueTrackerOnlySurfaceWarning, otherWarning } from './warnings.js';\n\nexport interface TrackerIssueResult {\n\tkey: string;\n\t/**\n\t * The resolved issue, or `undefined` when it provably does not exist or is not visible to this connection.\n\t * A failed read returns no item and sets `fetchFailed`.\n\t */\n\tissue?: IssueShape;\n}\n\n/**\n * `domain` selects the host of a self-managed tracker (Jira Data Center), as it does for\n * `listIssueTrackerIssuesPage`, and is ignored for the cloud trackers, which have a single canonical host.\n *\n * Unlike its siblings, this read never falls back to the primary connection for a self-managed tracker: it\n * requires a `domain` or a `connectionId` with a configured host and refuses otherwise. Two self-hosted\n * instances routinely issue the same project and issue keys, and this read's `issue: undefined` is a proven\n * absence a caller may cache — an answer from whichever host happens to be primary would be cached under a key\n * that names a different instance (#5872).\n */\nexport async function getTrackerIssue(\n\tctx: ProviderReadContext,\n\toptions: {\n\t\tproviderId: IntegrationIds;\n\t\tresourceId: string;\n\t\tresourceUrl?: string;\n\t\tkey: string;\n\t\tconnectionId?: string;\n\t\tdomain?: string;\n\t},\n): Promise<ProviderResult<TrackerIssueResult>> {\n\tconst refused = (warning: ProviderWarning): ProviderResult<TrackerIssueResult> => ({\n\t\titems: [],\n\t\twarnings: [warning],\n\t\tfetchFailed: true,\n\t});\n\tconst surface = 'Issue resolution by key';\n\n\tif (!isIssuesHostIntegrationId(options.providerId)) {\n\t\treturn refused(issueTrackerOnlySurfaceWarning(options.providerId, options.connectionId, surface));\n\t}\n\n\tif (options.resourceId.trim().length === 0) {\n\t\treturn refused(\n\t\t\totherWarning(options.providerId, undefined, options.connectionId, `${surface} requires a resource id.`),\n\t\t);\n\t}\n\n\tif (options.key.trim().length === 0) {\n\t\treturn refused(\n\t\t\totherWarning(options.providerId, undefined, options.connectionId, `${surface} requires an issue key.`),\n\t\t);\n\t}\n\n\tconst resourceUrl = options.resourceUrl?.trim() || undefined;\n\tif (options.providerId === IssuesCloudHostIntegrationId.Jira && resourceUrl == null) {\n\t\treturn refused(\n\t\t\totherWarning(\n\t\t\t\toptions.providerId,\n\t\t\t\tundefined,\n\t\t\t\toptions.connectionId,\n\t\t\t\t`${surface} requires the Jira resource URL so the result contains a browser link without resource discovery.`,\n\t\t\t),\n\t\t);\n\t}\n\n\t// A `domain` only selects a host when it parses to one, and a `connectionId` only when it names a configured\n\t// connection that has one; anything else would resolve the primary host instead, which is the fallback this\n\t// read refuses.\n\tif (\n\t\tisIssuesSelfManagedHostIntegrationId(options.providerId) &&\n\t\t(options.domain != null\n\t\t\t? hostFromDomain(options.domain) == null\n\t\t\t: options.connectionId == null ||\n\t\t\t\t!ctx\n\t\t\t\t\t.getConfigured(options.providerId)\n\t\t\t\t\t.some(c => c.id === options.connectionId && hostFromDomain(c.domain) != null))\n\t) {\n\t\treturn refused(\n\t\t\totherWarning(\n\t\t\t\toptions.providerId,\n\t\t\t\tundefined,\n\t\t\t\toptions.connectionId,\n\t\t\t\t`${surface} requires a domain or a configured connection id for '${options.providerId}': every configured host can hold a different issue under the same key, so the read does not answer from the primary connection.`,\n\t\t\t),\n\t\t);\n\t}\n\n\tconst integration = await ctx.getIntegrationForRead(options.providerId, options.connectionId, options.domain);\n\tif (integration == null) {\n\t\t// A supplied connectionId or domain that no longer resolves is a broken target, not an empty account.\n\t\tconst early = ctx.earlyReturnConnectionWarnings(options.providerId, options.connectionId, options.domain);\n\t\treturn { items: [], warnings: early.warnings, fetchFailed: early.fetchFailed || undefined };\n\t}\n\tif (!isIssuesIntegration(integration)) {\n\t\treturn refused(issueTrackerOnlySurfaceWarning(options.providerId, options.connectionId, surface));\n\t}\n\tif (!integration.supportsIssueLookupByResourceId) {\n\t\treturn refused(\n\t\t\totherWarning(\n\t\t\t\toptions.providerId,\n\t\t\t\tundefined,\n\t\t\t\toptions.connectionId,\n\t\t\t\t`${surface} is not supported by '${options.providerId}'; its single-issue read cannot prove an absence, so a miss would not be safe to cache.`,\n\t\t\t),\n\t\t);\n\t}\n\n\t// A self-managed tracker's single resource is its host, and the read is keyed (and cached) by `resourceId`\n\t// while it is addressed to the host `domain`/`connectionId` resolved. When they disagree, host B's answer —\n\t// including a cacheable absence — would be stored under host A's key.\n\tif (\n\t\tisIssuesSelfManagedHostIntegrationId(options.providerId) &&\n\t\t!areDomainsOnSameHost(options.resourceId, integration.domain)\n\t) {\n\t\treturn refused(\n\t\t\totherWarning(\n\t\t\t\toptions.providerId,\n\t\t\t\tundefined,\n\t\t\t\toptions.connectionId,\n\t\t\t\t`${surface} requires the resource id of '${options.providerId}' to name the host the read resolved to.`,\n\t\t\t),\n\t\t);\n\t}\n\n\tconst domain = ctx.domainForRead(integration, options.providerId, options.connectionId, options.domain);\n\tconst issue = await runCaptured(\n\t\toptions.providerId,\n\t\tdomain,\n\t\toptions.connectionId,\n\t\t() =>\n\t\t\tintegration.getIssueByResourceIdResult(options.resourceId, options.key, {\n\t\t\t\tconnectionId: options.connectionId,\n\t\t\t\tresourceUrl: resourceUrl,\n\t\t\t}),\n\t\t{ warnOnMissingSession: true },\n\t);\n\tif (issue.warning != null) {\n\t\treturn refused(issue.warning);\n\t}\n\n\treturn {\n\t\titems: [{ key: options.key, ...(issue.value != null ? { issue: issue.value } : {}) }],\n\t\twarnings: [],\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8BA,eAAsB,gBACrB,KACA,SAQ8C;CAC9C,MAAM,WAAW,aAAkE;EAClF,OAAO,CAAC;EACR,UAAU,CAAC,OAAO;EAClB,aAAa;CACd;CACA,MAAM,UAAU;CAEhB,IAAI,CAAC,0BAA0B,QAAQ,UAAU,GAChD,OAAO,QAAQ,+BAA+B,QAAQ,YAAY,QAAQ,cAAc,OAAO,CAAC;CAGjG,IAAI,QAAQ,WAAW,KAAK,CAAC,CAAC,WAAW,GACxC,OAAO,QACN,aAAa,QAAQ,YAAY,KAAA,GAAW,QAAQ,cAAc,GAAG,QAAQ,yBAAyB,CACvG;CAGD,IAAI,QAAQ,IAAI,KAAK,CAAC,CAAC,WAAW,GACjC,OAAO,QACN,aAAa,QAAQ,YAAY,KAAA,GAAW,QAAQ,cAAc,GAAG,QAAQ,wBAAwB,CACtG;CAGD,MAAM,cAAc,QAAQ,aAAa,KAAK,KAAK,KAAA;CACnD,IAAI,QAAQ,eAAA,UAAoD,eAAe,MAC9E,OAAO,QACN,aACC,QAAQ,YACR,KAAA,GACA,QAAQ,cACR,GAAG,QAAQ,kGACZ,CACD;CAMD,IACC,qCAAqC,QAAQ,UAAU,MACtD,QAAQ,UAAU,OAChB,eAAe,QAAQ,MAAM,KAAK,OAClC,QAAQ,gBAAgB,QACzB,CAAC,IACC,cAAc,QAAQ,UAAU,CAAC,CACjC,MAAK,MAAK,EAAE,OAAO,QAAQ,gBAAgB,eAAe,EAAE,MAAM,KAAK,IAAI,IAE/E,OAAO,QACN,aACC,QAAQ,YACR,KAAA,GACA,QAAQ,cACR,GAAG,QAAQ,wDAAwD,QAAQ,WAAW,iIACvF,CACD;CAGD,MAAM,cAAc,MAAM,IAAI,sBAAsB,QAAQ,YAAY,QAAQ,cAAc,QAAQ,MAAM;CAC5G,IAAI,eAAe,MAAM;EAExB,MAAM,QAAQ,IAAI,8BAA8B,QAAQ,YAAY,QAAQ,cAAc,QAAQ,MAAM;EACxG,OAAO;GAAE,OAAO,CAAC;GAAG,UAAU,MAAM;GAAU,aAAa,MAAM,eAAe,KAAA;EAAU;CAC3F;CACA,IAAI,CAAC,oBAAoB,WAAW,GACnC,OAAO,QAAQ,+BAA+B,QAAQ,YAAY,QAAQ,cAAc,OAAO,CAAC;CAEjG,IAAI,CAAC,YAAY,iCAChB,OAAO,QACN,aACC,QAAQ,YACR,KAAA,GACA,QAAQ,cACR,GAAG,QAAQ,wBAAwB,QAAQ,WAAW,wFACvD,CACD;CAMD,IACC,qCAAqC,QAAQ,UAAU,KACvD,CAAC,qBAAqB,QAAQ,YAAY,YAAY,MAAM,GAE5D,OAAO,QACN,aACC,QAAQ,YACR,KAAA,GACA,QAAQ,cACR,GAAG,QAAQ,gCAAgC,QAAQ,WAAW,yCAC/D,CACD;CAGD,MAAM,SAAS,IAAI,cAAc,aAAa,QAAQ,YAAY,QAAQ,cAAc,QAAQ,MAAM;CACtG,MAAM,QAAQ,MAAM,YACnB,QAAQ,YACR,QACA,QAAQ,oBAEP,YAAY,2BAA2B,QAAQ,YAAY,QAAQ,KAAK;EACvE,cAAc,QAAQ;EACT;CACd,CAAC,GACF,EAAE,sBAAsB,KAAK,CAC9B;CACA,IAAI,MAAM,WAAW,MACpB,OAAO,QAAQ,MAAM,OAAO;CAG7B,OAAO;EACN,OAAO,CAAC;GAAE,KAAK,QAAQ;GAAK,GAAI,MAAM,SAAS,OAAO,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;EAAG,CAAC;EACpF,UAAU,CAAC;CACZ;AACD"}
|
|
@@ -145,9 +145,13 @@ export declare function unmergeableIssueSortWarning(id: IntegrationIds, domain:
|
|
|
145
145
|
* Returns `undefined` when the provider declares no ceiling, so the caller falls back to the generic wording
|
|
146
146
|
* rather than reporting a limit it invented.
|
|
147
147
|
*/
|
|
148
|
-
export declare function issueSearchCapResultWarning(id: IntegrationIds, domain: string | undefined, connectionId: string | undefined, totalCount: number | undefined, sort: IssueSorting
|
|
148
|
+
export declare function issueSearchCapResultWarning(id: IntegrationIds, domain: string | undefined, connectionId: string | undefined, totalCount: number | undefined, sort: IssueSorting,
|
|
149
|
+
/** The provider proved the ceiling was reached without reporting how many matched; see `limitReached`. */
|
|
150
|
+
limitReached?: boolean): ProviderWarning | undefined;
|
|
149
151
|
/** Quantified omission for a pull-request search that exceeded the provider's reachable result window. */
|
|
150
|
-
export declare function pullRequestSearchCapResultWarning(id: IntegrationIds, domain: string | undefined, connectionId: string | undefined, totalCount: number | undefined, sort: PullRequestSorting
|
|
152
|
+
export declare function pullRequestSearchCapResultWarning(id: IntegrationIds, domain: string | undefined, connectionId: string | undefined, totalCount: number | undefined, sort: PullRequestSorting,
|
|
153
|
+
/** See {@link issueSearchCapResultWarning}. */
|
|
154
|
+
limitReached?: boolean): ProviderWarning | undefined;
|
|
151
155
|
/** Warning for a git host that doesn't expose issues on this surface (e.g. Bitbucket, deprecated in favor of Jira). */
|
|
152
156
|
export declare function issuesUnsupportedWarning(id: IntegrationIds, domain: string | undefined, connectionId: string | undefined): ProviderWarning;
|
|
153
157
|
//#endregion
|
|
@@ -270,37 +270,33 @@ function unmergeableIssueSortWarning(id, domain, connectionId, unmergeable, scop
|
|
|
270
270
|
* Returns `undefined` when the provider declares no ceiling, so the caller falls back to the generic wording
|
|
271
271
|
* rather than reporting a limit it invented.
|
|
272
272
|
*/
|
|
273
|
-
function issueSearchCapResultWarning(id, domain, connectionId, totalCount, sort) {
|
|
274
|
-
|
|
273
|
+
function issueSearchCapResultWarning(id, domain, connectionId, totalCount, sort, limitReached) {
|
|
274
|
+
return searchCapResultWarning(id, domain, connectionId, "Issue search", providersMetadata[id]?.issueSearchResultLimit, totalCount, sort, limitReached);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* The shared body of the two ceiling warnings. Only a ceiling that was actually reached warns: either a reported
|
|
278
|
+
* total above it, or a provider that proved the ceiling was reached but can't count past it — in which case the
|
|
279
|
+
* omission carries the limit and the order but no `totalCount`, since any figure would be invented.
|
|
280
|
+
*/
|
|
281
|
+
function searchCapResultWarning(id, domain, connectionId, readKind, limit, totalCount, sort, limitReached) {
|
|
275
282
|
if (limit == null) return void 0;
|
|
276
|
-
|
|
283
|
+
const counted = totalCount != null && totalCount > limit;
|
|
284
|
+
if (!counted && limitReached !== true) return void 0;
|
|
277
285
|
const [field, direction] = sort.split(":");
|
|
278
286
|
return {
|
|
279
|
-
...otherWarning(id, domain, connectionId,
|
|
287
|
+
...otherWarning(id, domain, connectionId, `${readKind} ${counted ? `matched ${totalCount} results` : `matched more than ${limit} results`}, but '${id}' serves at most ${limit}, ordered by ${field} ${direction}ending; narrow the search to read the rest.`),
|
|
280
288
|
omission: {
|
|
281
289
|
kind: "provider-limit",
|
|
282
290
|
recovery: "none",
|
|
283
291
|
limit,
|
|
284
|
-
totalCount,
|
|
292
|
+
...counted ? { totalCount } : {},
|
|
285
293
|
sort
|
|
286
294
|
}
|
|
287
295
|
};
|
|
288
296
|
}
|
|
289
297
|
/** Quantified omission for a pull-request search that exceeded the provider's reachable result window. */
|
|
290
|
-
function pullRequestSearchCapResultWarning(id, domain, connectionId, totalCount, sort) {
|
|
291
|
-
|
|
292
|
-
if (limit == null || totalCount == null || totalCount <= limit) return void 0;
|
|
293
|
-
const [field, direction] = sort.split(":");
|
|
294
|
-
return {
|
|
295
|
-
...otherWarning(id, domain, connectionId, `Pull request search matched ${totalCount} results, but '${id}' serves at most ${limit}, ordered by ${field} ${direction}ending; narrow the search to read the rest.`),
|
|
296
|
-
omission: {
|
|
297
|
-
kind: "provider-limit",
|
|
298
|
-
recovery: "none",
|
|
299
|
-
limit,
|
|
300
|
-
totalCount,
|
|
301
|
-
sort
|
|
302
|
-
}
|
|
303
|
-
};
|
|
298
|
+
function pullRequestSearchCapResultWarning(id, domain, connectionId, totalCount, sort, limitReached) {
|
|
299
|
+
return searchCapResultWarning(id, domain, connectionId, "Pull request search", providersMetadata[id]?.pullRequestSearchResultLimit, totalCount, sort, limitReached);
|
|
304
300
|
}
|
|
305
301
|
/** Warning for a git host that doesn't expose issues on this surface (e.g. Bitbucket, deprecated in favor of Jira). */
|
|
306
302
|
function issuesUnsupportedWarning(id, domain, connectionId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warnings.js","names":[],"sources":["../../../../../plus/integrations/src/reads/warnings.ts"],"sourcesContent":["import type { IntegrationIds } from '../constants.js';\nimport type {\n\tIssueFilter,\n\tIssueSearchCapabilities,\n\tIssueSorting,\n\tPullRequestFilter,\n\tPullRequestSearchCapabilities,\n\tPullRequestSorting,\n} from '../providerFilters.js';\nimport { providersMetadata } from '../providers/models.js';\nimport type { ProviderWarning, ProviderWarningOmissionRecovery } from '../results.js';\nimport type { IssueSearchCriteriaRejection, PullRequestSearchCriteriaRejection } from './filters.js';\nimport type { UnmergeableIssueSort, UnsupportedIssueSortRejection } from './ordering.js';\n\n/**\n * The warnings the provider facade raises on its OWN terms, as opposed to the ones derived from a caught\n * provider error ({@link toProviderWarning}) or from SDK collection metadata (`collectionMetadata.ts`).\n *\n * These are pure message builders, kept together and out of the service so a read method reads as the decision\n * it makes (\"this surface doesn't apply, refuse it\") rather than as the wording of the refusal, and so the same\n * refusal can't be phrased two ways in two reads.\n */\n\n/**\n * The single builder for a provider-neutral, non-auth warning: an unsupported capability, a\n * contradictory/inexpressible request, or a read that couldn't confirm completeness. Every `kind: 'other'`\n * warning the facade raises on its own terms goes through here (directly, or via one of the named builders\n * below that pin a recurring message), so the discriminant is assigned in one place. The warnings mapped from\n * SDK collection metadata are the documented exception — `collectionMetadata.ts` builds those from the\n * structured failure/omission it is given.\n *\n * The kinds that carry a programmatic remedy — `auth`, `rate-limit`, `not-found`, `no-connection` — are derived\n * from the caught error's type instead and never come from here; see `ProviderWarningKind` and\n * {@link noConnectionWarning}.\n */\nexport function otherWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tmessage: string,\n): ProviderWarning {\n\treturn {\n\t\tproviderId: id,\n\t\tdomain: domain,\n\t\tconnectionId: connectionId,\n\t\tmessage: message,\n\t\tkind: 'other',\n\t\tisAuth: false,\n\t};\n}\n\n/**\n * Builds a `no-connection` warning for a per-connection read that resolved neither a session nor an error: the\n * requested `connectionId` no longer resolves (deleted, or its authentication is invalid). Consumers use this\n * to tell a truly empty account apart from a broken connection.\n */\nexport function noConnectionWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId?: string,\n): ProviderWarning {\n\treturn {\n\t\tproviderId: id,\n\t\tdomain: domain,\n\t\tconnectionId: connectionId,\n\t\tmessage:\n\t\t\tconnectionId != null\n\t\t\t\t? `Connection '${connectionId}' for '${id}' could not be resolved (deleted or invalid authentication).`\n\t\t\t\t: `No active connection for '${id}' could be resolved.`,\n\t\tkind: 'no-connection',\n\t\tisAuth: false,\n\t};\n}\n\n/** Warning for an issue-tracker provider asked for a surface only a git host serves (repos, PRs, sweeps). */\nexport function gitHostOnlySurfaceWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tsurface: string,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t`${surface} is not supported by '${id}'; use a git-host integration instead.`,\n\t);\n}\n\n/** Warning for a git host asked for a surface only an issue tracker serves (resource → project issue reads). */\nexport function issueTrackerOnlySurfaceWarning(\n\tid: IntegrationIds,\n\tconnectionId: string | undefined,\n\tsurface: string,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tundefined,\n\t\tconnectionId,\n\t\t`${surface} is not supported by '${id}'; use an issue-tracker integration instead.`,\n\t);\n}\n\n/** Why a facade-driven read returned fewer results than requested. */\nexport type IncompleteReadCause = 'interrupted' | 'page-budget' | 'exhausted' | 'scope-too-large';\n\nfunction recoveryForIncompleteReadCause(cause: IncompleteReadCause): ProviderWarningOmissionRecovery {\n\tswitch (cause) {\n\t\tcase 'page-budget':\n\t\t\treturn 'page-budget';\n\t\tcase 'scope-too-large':\n\t\t\treturn 'narrow-scope';\n\t\tcase 'interrupted':\n\t\tcase 'exhausted':\n\t\t\treturn 'none';\n\t\tdefault:\n\t\t\tcause satisfies never;\n\t\t\treturn 'none';\n\t}\n}\n\n/**\n * Builds the warning for a read that returned less than everything, carrying the two facts a consumer cannot\n * recover on its own: did the request succeed, and would anything fetch the rest?\n *\n * Every incompleteness warning the facade raises on its own terms goes through here, so no read can drift\n * into claiming success on a failure or offering a \"load more\" that cannot deliver. The omission it attaches\n * is the same shape `assessCollectionMetadata` derives from SDK metadata, so a consumer never has to care\n * which layer noticed the gap.\n *\n * No `scope`: this applies to the whole read, not to one repository or project the way an SDK-attributed\n * omission can, so a consumer acting on `recovery` re-runs the read itself.\n */\nexport function incompleteReadWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tmessage: string,\n\tcause: IncompleteReadCause,\n): ProviderWarning {\n\tconst warning = otherWarning(id, domain, connectionId, message);\n\tif (cause === 'interrupted') return warning;\n\n\treturn {\n\t\t...warning,\n\t\tomission: {\n\t\t\tkind: 'pagination-incomplete',\n\t\t\trecovery: recoveryForIncompleteReadCause(cause),\n\t\t},\n\t};\n}\n\n/**\n * The read surfaces {@link truncationWarning} phrases for. `'Account-wide issue search'` names the composite\n * read that spans several provider searches rather than one surface.\n *\n * Deliberately not exported: every caller passes a literal, and publishing it would invite a consumer to switch\n * on a set that exists only to word a message.\n */\ntype TruncatedReadKind =\n\t| 'Pull request'\n\t| 'Issue'\n\t| 'Repository'\n\t| 'Organization'\n\t| 'Project'\n\t| 'Account-wide issue search'\n\t| 'Issue search'\n\t| 'Pull request search';\n\n/** {@link incompleteReadWarning} for a paged or drained read, phrased per surface. */\nexport function truncationWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\treadKind: TruncatedReadKind,\n\t/**\n\t * Deliberately REQUIRED and not defaulted: a default would make one of these the silent fallback at any\n\t * call site that forgot it, and both of the claims it carries — \"the request succeeded\" and \"more can be\n\t * fetched\" — are ones this warning must never make by accident.\n\t */\n\tcause: IncompleteReadCause,\n): ProviderWarning {\n\treturn incompleteReadWarning(id, domain, connectionId, truncationMessage(id, readKind, cause), cause);\n}\n\nfunction truncationMessage(id: IntegrationIds, readKind: TruncatedReadKind, cause: IncompleteReadCause): string {\n\tswitch (cause) {\n\t\tcase 'interrupted':\n\t\t\t// Deliberately does not name a mechanism. This fires both when a page was lost mid-drain and when a\n\t\t\t// drain that latched an earlier scope failure later stopped for its own reasons; \"was interrupted\"\n\t\t\t// would be false in the second. What is true in both is that something failed and results are gone.\n\t\t\treturn `${readKind} read for '${id}' did not complete; some results are missing and the read reported a failure.`;\n\t\tcase 'page-budget':\n\t\t\treturn `${readKind} read for '${id}' stopped at its page budget; more results can be read by raising it.`;\n\t\tcase 'exhausted':\n\t\t\treturn `${readKind} read for '${id}' was truncated and cannot be continued; results may be incomplete.`;\n\t\tcase 'scope-too-large':\n\t\t\t// Says what is true — the scope is larger than a read of it — rather than blaming the provider for a\n\t\t\t// ceiling that is ours, and names the remedy that exists, since no retry or budget has one.\n\t\t\treturn `${readKind} read for '${id}' covers more than one read can return; narrow the scope to read the rest.`;\n\t}\n\t// No `default`: `IncompleteReadCause` is declared in this file, so `noImplicitReturns` already fails the\n\t// build here if a cause is added without its own wording. (`collectionOmissionMessage`'s `satisfies never`\n\t// is not the same case — that union is the SDK's, and can widen under a dependency bump.)\n}\n\n/**\n * The one sentence every \"you asked for a narrowing this provider can't express\" refusal uses.\n *\n * Shared because the three callers below differ only in a noun: the wording is consumer-visible, and three\n * hand-written copies drift three ways — including the `(supported: …)` clause, which exists to avoid emitting an\n * empty parenthetical and has to be the same guard in all of them.\n */\nfunction unsupportedNarrowingMessage(id: IntegrationIds, noun: string, requested: string, supported: string): string {\n\treturn `The requested ${noun} (${requested}) are not supported by '${id}'${supported.length > 0 ? ` (supported: ${supported})` : ''}; skipped to avoid returning a wider result than requested.`;\n}\n\n/** Warning for an account-wide issue read whose requested filters the provider can't express server-side. */\nexport function unsupportedAccountWideIssueFiltersWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tfilters: IssueFilter[],\n): ProviderWarning {\n\tconst supported = providersMetadata[id]?.supportedAccountWideIssueFilters ?? [];\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\tunsupportedNarrowingMessage(id, 'account-wide issue filters', filters.join(', '), supported.join(', ')),\n\t);\n}\n\n/** Warning for an account-wide PR read whose requested relationship union cannot be expressed exactly. */\nexport function unsupportedAccountWidePullRequestFiltersWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tfilters: PullRequestFilter[],\n): ProviderWarning {\n\tconst supported = providersMetadata[id]?.supportedAccountWidePullRequestFilters ?? [];\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\tunsupportedNarrowingMessage(id, 'account-wide pull request filters', filters.join(', '), supported.join(', ')),\n\t);\n}\n\n/** Warning for a repo-scoped PR read whose requested filters the provider supports none of. */\nexport function unsupportedFiltersWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t`The requested pull request filters are not supported by '${id}'; skipped to avoid returning unfiltered results.`,\n\t);\n}\n\n/**\n * The one sentence every `unusable-scope` refusal uses — the five surfaces that validate a search scope (both\n * searches, both count probes, and issue broadening) differ only in the noun and in whether they name a count\n * key, so the reason a scope is unusable is written once. `prefix` is what the count probes put in front of it\n * to name their key.\n *\n * NAMES the offending values, because which failure mode applied is not deducible from the value: a quote makes\n * `git\"kraken` a request for the real and different org `gitkraken`, a space makes `my org` a search of `my`\n * filtered by the text `org`, and a value of only quotes or whitespace leaves no scope at all. The caller is the\n * only layer that knows which scope it meant.\n *\n * Says how to fix it rather than what was stripped: a consumer cannot mirror this rule (unlike free-text\n * sanitizing, whose rules are published for exactly that), so \"pass the scope as the provider names it\" is the\n * actionable half.\n */\nexport function unusableSearchScopeMessage(noun: string, scopes: readonly string[], prefix?: string): string {\n\treturn `${prefix != null ? `${prefix}: ` : ''}The requested ${noun} (${scopes.map(s => JSON.stringify(s)).join(', ')}) cannot be used as given: a scope name cannot contain quotes, or an inner space or control character, which a query would drop or read as a second qualifier — searching a different scope, or none. Pass the scope exactly as the provider names it.`;\n}\n\n/** Warning for a filtered pull-request search the provider cannot run as requested. */\nexport function unsupportedPullRequestSearchCriteriaWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\trejection: PullRequestSearchCriteriaRejection,\n): ProviderWarning {\n\tlet message: string;\n\tswitch (rejection.reason) {\n\t\tcase 'unsupported-search':\n\t\t\tmessage = `Filtered pull request search is not supported by '${id}'; use \\`listPullRequestsPage\\` for its own pull request reads.`;\n\t\t\tbreak;\n\t\tcase 'unsupported-criteria': {\n\t\t\tconst supported = providersMetadata[id]?.supportedPullRequestSearch;\n\t\t\tmessage = unsupportedNarrowingMessage(\n\t\t\t\tid,\n\t\t\t\t'pull request search criteria',\n\t\t\t\trejection.criteria.join(', '),\n\t\t\t\tsupported != null ? describePullRequestSearchCapabilities(supported) : '',\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn otherWarning(id, domain, connectionId, message);\n}\n\nfunction describePullRequestSearchCapabilities(capabilities: PullRequestSearchCapabilities): string {\n\treturn [\n\t\t...(capabilities.relationships.length ? [`relationships:${capabilities.relationships.join('|')}`] : []),\n\t\t...(capabilities.states.length ? [`states:${capabilities.states.join('|')}`] : []),\n\t\t...(capabilities.text ? ['text'] : []),\n\t\t...(capabilities.updatedAfter ? ['updatedAfter'] : []),\n\t\t...(capabilities.createdAfter ? ['createdAfter'] : []),\n\t\t...(capabilities.includeArchived ? ['includeArchived'] : []),\n\t\t...(capabilities.draft ? ['draft'] : []),\n\t\t...(capabilities.repositoryScope ? ['repository scope'] : []),\n\t\t...(capabilities.organizationScope ? ['organization scope'] : []),\n\t\t...(capabilities.sorts.length ? [`sorts:${capabilities.sorts.join('|')}`] : []),\n\t].join(', ');\n}\n\n/**\n * Warning for a filtered issue search whose criteria the provider can't serve — either it has no such search at\n * all, or it can't express what was asked for, or the set contradicts itself.\n *\n * One builder for all three because they are one decision from the caller's side (\"this search can't be run as\n * asked\") and the remedy differs only in what the message says. The rejection shape carries which case it is, so\n * the wording can't drift from the check that produced it (see {@link resolveIssueSearchCriteria}).\n */\nexport function unsupportedIssueSearchCriteriaWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\trejection: IssueSearchCriteriaRejection,\n): ProviderWarning {\n\tlet message: string;\n\tswitch (rejection.reason) {\n\t\tcase 'unsupported-search':\n\t\t\tmessage = `Filtered issue search is not supported by '${id}'; use \\`listIssuesPage\\` for its own issue reads.`;\n\t\t\tbreak;\n\t\tcase 'unsupported-criteria': {\n\t\t\tconst supported = providersMetadata[id]?.supportedIssueSearch;\n\t\t\tmessage = unsupportedNarrowingMessage(\n\t\t\t\tid,\n\t\t\t\t'issue search criteria',\n\t\t\t\trejection.criteria.join(', '),\n\t\t\t\tsupported != null ? describeIssueSearchCapabilities(supported) : '',\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t\tcase 'contradictory-relationships':\n\t\t\tmessage = `The relationships \\`any-assignee\\` and \\`unassigned\\` are contradictory — they partition the scope between them, so no issue satisfies both; pass only one.`;\n\t\t\tbreak;\n\t}\n\t// No `default`: the union is declared alongside the validator, so `noImplicitReturns` fails the build here if\n\t// a rejection reason is added without its own wording.\n\n\treturn otherWarning(id, domain, connectionId, message);\n}\n\n/** The criteria a provider CAN express, for the \"supported: …\" half of a refusal message. */\nfunction describeIssueSearchCapabilities(capabilities: IssueSearchCapabilities): string {\n\tconst flags: [name: string, supported: boolean][] = [\n\t\t['text', capabilities.text],\n\t\t['labels', capabilities.labels],\n\t\t['milestone', capabilities.milestone],\n\t\t['updatedAfter', capabilities.updatedAfter],\n\t\t['createdAfter', capabilities.createdAfter],\n\t\t['withoutLinkedPullRequest', capabilities.withoutLinkedPullRequest],\n\t\t['state', capabilities.states],\n\t];\n\treturn [\n\t\t...capabilities.relationships.map(r => `relationships:${r}`),\n\t\t...flags.filter(([, supported]) => supported).map(([name]) => name),\n\t\t...capabilities.sorts.map(s => `sort:${s}`),\n\t].join(', ');\n}\n\n/**\n * Warning for an issue read asked to order by a key its provider can't express server-side.\n *\n * Separate from {@link unsupportedIssueSearchCriteriaWarning} because it serves the reads that take `sort` as an\n * OPTION rather than as a criterion — repo-scoped, account-wide, and issue-tracker — where there is no criteria\n * set to fold it into. The filtered search keeps reporting it as one more `unsupported-criteria` entry, so a\n * caller there still gets a single refusal listing everything at once.\n *\n * Takes the rejection the check produced rather than a re-read supported list: which of the two tables applies is\n * the caller's own branch (`repos` present or not), so reading it back here — or threading it by hand — would be\n * free to disagree with the check that refused.\n */\nexport function unsupportedIssueSortWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\trejection: UnsupportedIssueSortRejection,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\tunsupportedNarrowingMessage(id, 'issue sort', rejection.requested, (rejection.supported ?? []).join(', ')),\n\t);\n}\n\n/**\n * Warning for a merged issue read asked to order by a key that no normalized issue carries.\n *\n * The distinction this exists to make is not about the provider: the provider orders by `priority` perfectly well\n * within ONE project. It is about the read, which fans out over several and merges the results here, where the\n * only fields available are the ones {@link IssueShape} models. Serving the concatenated per-scope runs would look\n * ordered without being so, which is why this refuses instead — and why the message says WHY, since the same key\n * against the same provider succeeds on a single-scope read.\n *\n * What a read fans out OVER differs by read, hence the `scope`: repositories, projects, or — where one query is\n * issued per requested relationship and the runs are unioned within a single project (Jira) — the filters\n * themselves. Naming the wrong one would tell a caller to narrow something that isn't what merged.\n */\nexport function unmergeableIssueSortWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tunmergeable: UnmergeableIssueSort,\n\tscope: 'repositories' | 'projects' | 'filters',\n): ProviderWarning {\n\t// Both forms are spelled out rather than depluralized at runtime: the message needs each, and\n\t// `'repositories'.replace(/s$/, '')` is `'repositorie'`.\n\tconst nouns = {\n\t\trepositories: { many: 'repositories', one: 'repository' },\n\t\tprojects: { many: 'projects', one: 'project' },\n\t\tfilters: { many: 'issue relationships', one: 'relationship' },\n\t}[scope];\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t`Cannot order by '${unmergeable.requested}' across several ${nouns.many}: this read merges their results, and a normalized issue carries no such field to merge on. Read one ${nouns.one} at a time, or order by a date, title, comment or reaction count.`,\n\t);\n}\n\n/**\n * The omission for a filtered issue search that hit the provider's RESULT CEILING: more issues matched than the\n * provider will ever serve for one query, however it is paged.\n *\n * Distinct from {@link truncationWarning} because this is the one incompleteness this read can quantify —\n * `totalCount` is how many matched, `limit` how many are reachable — which is what lets a consumer say \"19.240\n * matched, showing the 1.000 most recently updated\" instead of a bare \"results may be incomplete\". `recovery:\n * 'none'` because the items past the ceiling are UNREACHABLE, not merely unfetched: no budget, no retry, and no\n * cursor would return them. Narrowing the criteria is the only way through, which is a decision for the caller.\n *\n * The message names the ORDER, and has to now that ordering is an option: the reachable window is the top `limit`\n * under that key, so \"the 1.000 most recent\" is a true sentence only while the key is the default one. The same\n * value goes on the omission, so a consumer wording its own sentence doesn't have to remember what it asked for.\n *\n * Returns `undefined` when the provider declares no ceiling, so the caller falls back to the generic wording\n * rather than reporting a limit it invented.\n */\nexport function issueSearchCapResultWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\ttotalCount: number | undefined,\n\tsort: IssueSorting,\n): ProviderWarning | undefined {\n\tconst limit = providersMetadata[id]?.issueSearchResultLimit;\n\tif (limit == null) return undefined;\n\t// Below the ceiling this warning would be a false claim; the caller's truncation had another cause.\n\tif (totalCount == null || totalCount <= limit) return undefined;\n\n\tconst [field, direction] = sort.split(':');\n\treturn {\n\t\t...otherWarning(\n\t\t\tid,\n\t\t\tdomain,\n\t\t\tconnectionId,\n\t\t\t`Issue search matched ${totalCount} results, but '${id}' serves at most ${limit}, ordered by ${field} ${direction}ending; narrow the search to read the rest.`,\n\t\t),\n\t\tomission: {\n\t\t\tkind: 'provider-limit',\n\t\t\t// The items past the ceiling can't be fetched by anything, so never offer a \"load more\".\n\t\t\trecovery: 'none',\n\t\t\tlimit: limit,\n\t\t\ttotalCount: totalCount,\n\t\t\tsort: sort,\n\t\t},\n\t};\n}\n\n/** Quantified omission for a pull-request search that exceeded the provider's reachable result window. */\nexport function pullRequestSearchCapResultWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\ttotalCount: number | undefined,\n\tsort: PullRequestSorting,\n): ProviderWarning | undefined {\n\tconst limit = providersMetadata[id]?.pullRequestSearchResultLimit;\n\tif (limit == null || totalCount == null || totalCount <= limit) return undefined;\n\n\t// Name the order: the reachable window is the first `limit` under THIS sort, and a different key reaches a\n\t// different subset, so a ceiling message that omits it can't say which slice was served. Same reasoning as\n\t// `issueSearchCapResultWarning`.\n\tconst [field, direction] = sort.split(':');\n\treturn {\n\t\t...otherWarning(\n\t\t\tid,\n\t\t\tdomain,\n\t\t\tconnectionId,\n\t\t\t`Pull request search matched ${totalCount} results, but '${id}' serves at most ${limit}, ordered by ${field} ${direction}ending; narrow the search to read the rest.`,\n\t\t),\n\t\tomission: {\n\t\t\tkind: 'provider-limit',\n\t\t\trecovery: 'none',\n\t\t\tlimit: limit,\n\t\t\ttotalCount: totalCount,\n\t\t\tsort: sort,\n\t\t},\n\t};\n}\n\n/** Warning for a git host that doesn't expose issues on this surface (e.g. Bitbucket, deprecated in favor of Jira). */\nexport function issuesUnsupportedWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t`Issues are not supported by '${id}'; use a dedicated issue integration (e.g. Jira) instead.`,\n\t);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"warnings.js","names":[],"sources":["../../../../../plus/integrations/src/reads/warnings.ts"],"sourcesContent":["import type { IntegrationIds } from '../constants.js';\nimport type {\n\tIssueFilter,\n\tIssueSearchCapabilities,\n\tIssueSorting,\n\tPullRequestFilter,\n\tPullRequestSearchCapabilities,\n\tPullRequestSorting,\n} from '../providerFilters.js';\nimport { providersMetadata } from '../providers/models.js';\nimport type { ProviderWarning, ProviderWarningOmissionRecovery } from '../results.js';\nimport type { IssueSearchCriteriaRejection, PullRequestSearchCriteriaRejection } from './filters.js';\nimport type { UnmergeableIssueSort, UnsupportedIssueSortRejection } from './ordering.js';\n\n/**\n * The warnings the provider facade raises on its OWN terms, as opposed to the ones derived from a caught\n * provider error ({@link toProviderWarning}) or from SDK collection metadata (`collectionMetadata.ts`).\n *\n * These are pure message builders, kept together and out of the service so a read method reads as the decision\n * it makes (\"this surface doesn't apply, refuse it\") rather than as the wording of the refusal, and so the same\n * refusal can't be phrased two ways in two reads.\n */\n\n/**\n * The single builder for a provider-neutral, non-auth warning: an unsupported capability, a\n * contradictory/inexpressible request, or a read that couldn't confirm completeness. Every `kind: 'other'`\n * warning the facade raises on its own terms goes through here (directly, or via one of the named builders\n * below that pin a recurring message), so the discriminant is assigned in one place. The warnings mapped from\n * SDK collection metadata are the documented exception — `collectionMetadata.ts` builds those from the\n * structured failure/omission it is given.\n *\n * The kinds that carry a programmatic remedy — `auth`, `rate-limit`, `not-found`, `no-connection` — are derived\n * from the caught error's type instead and never come from here; see `ProviderWarningKind` and\n * {@link noConnectionWarning}.\n */\nexport function otherWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tmessage: string,\n): ProviderWarning {\n\treturn {\n\t\tproviderId: id,\n\t\tdomain: domain,\n\t\tconnectionId: connectionId,\n\t\tmessage: message,\n\t\tkind: 'other',\n\t\tisAuth: false,\n\t};\n}\n\n/**\n * Builds a `no-connection` warning for a per-connection read that resolved neither a session nor an error: the\n * requested `connectionId` no longer resolves (deleted, or its authentication is invalid). Consumers use this\n * to tell a truly empty account apart from a broken connection.\n */\nexport function noConnectionWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId?: string,\n): ProviderWarning {\n\treturn {\n\t\tproviderId: id,\n\t\tdomain: domain,\n\t\tconnectionId: connectionId,\n\t\tmessage:\n\t\t\tconnectionId != null\n\t\t\t\t? `Connection '${connectionId}' for '${id}' could not be resolved (deleted or invalid authentication).`\n\t\t\t\t: `No active connection for '${id}' could be resolved.`,\n\t\tkind: 'no-connection',\n\t\tisAuth: false,\n\t};\n}\n\n/** Warning for an issue-tracker provider asked for a surface only a git host serves (repos, PRs, sweeps). */\nexport function gitHostOnlySurfaceWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tsurface: string,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t`${surface} is not supported by '${id}'; use a git-host integration instead.`,\n\t);\n}\n\n/** Warning for a git host asked for a surface only an issue tracker serves (resource → project issue reads). */\nexport function issueTrackerOnlySurfaceWarning(\n\tid: IntegrationIds,\n\tconnectionId: string | undefined,\n\tsurface: string,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tundefined,\n\t\tconnectionId,\n\t\t`${surface} is not supported by '${id}'; use an issue-tracker integration instead.`,\n\t);\n}\n\n/** Why a facade-driven read returned fewer results than requested. */\nexport type IncompleteReadCause = 'interrupted' | 'page-budget' | 'exhausted' | 'scope-too-large';\n\nfunction recoveryForIncompleteReadCause(cause: IncompleteReadCause): ProviderWarningOmissionRecovery {\n\tswitch (cause) {\n\t\tcase 'page-budget':\n\t\t\treturn 'page-budget';\n\t\tcase 'scope-too-large':\n\t\t\treturn 'narrow-scope';\n\t\tcase 'interrupted':\n\t\tcase 'exhausted':\n\t\t\treturn 'none';\n\t\tdefault:\n\t\t\tcause satisfies never;\n\t\t\treturn 'none';\n\t}\n}\n\n/**\n * Builds the warning for a read that returned less than everything, carrying the two facts a consumer cannot\n * recover on its own: did the request succeed, and would anything fetch the rest?\n *\n * Every incompleteness warning the facade raises on its own terms goes through here, so no read can drift\n * into claiming success on a failure or offering a \"load more\" that cannot deliver. The omission it attaches\n * is the same shape `assessCollectionMetadata` derives from SDK metadata, so a consumer never has to care\n * which layer noticed the gap.\n *\n * No `scope`: this applies to the whole read, not to one repository or project the way an SDK-attributed\n * omission can, so a consumer acting on `recovery` re-runs the read itself.\n */\nexport function incompleteReadWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tmessage: string,\n\tcause: IncompleteReadCause,\n): ProviderWarning {\n\tconst warning = otherWarning(id, domain, connectionId, message);\n\tif (cause === 'interrupted') return warning;\n\n\treturn {\n\t\t...warning,\n\t\tomission: {\n\t\t\tkind: 'pagination-incomplete',\n\t\t\trecovery: recoveryForIncompleteReadCause(cause),\n\t\t},\n\t};\n}\n\n/**\n * The read surfaces {@link truncationWarning} phrases for. `'Account-wide issue search'` names the composite\n * read that spans several provider searches rather than one surface.\n *\n * Deliberately not exported: every caller passes a literal, and publishing it would invite a consumer to switch\n * on a set that exists only to word a message.\n */\ntype TruncatedReadKind =\n\t| 'Pull request'\n\t| 'Issue'\n\t| 'Repository'\n\t| 'Organization'\n\t| 'Project'\n\t| 'Account-wide issue search'\n\t| 'Issue search'\n\t| 'Pull request search';\n\n/** {@link incompleteReadWarning} for a paged or drained read, phrased per surface. */\nexport function truncationWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\treadKind: TruncatedReadKind,\n\t/**\n\t * Deliberately REQUIRED and not defaulted: a default would make one of these the silent fallback at any\n\t * call site that forgot it, and both of the claims it carries — \"the request succeeded\" and \"more can be\n\t * fetched\" — are ones this warning must never make by accident.\n\t */\n\tcause: IncompleteReadCause,\n): ProviderWarning {\n\treturn incompleteReadWarning(id, domain, connectionId, truncationMessage(id, readKind, cause), cause);\n}\n\nfunction truncationMessage(id: IntegrationIds, readKind: TruncatedReadKind, cause: IncompleteReadCause): string {\n\tswitch (cause) {\n\t\tcase 'interrupted':\n\t\t\t// Deliberately does not name a mechanism. This fires both when a page was lost mid-drain and when a\n\t\t\t// drain that latched an earlier scope failure later stopped for its own reasons; \"was interrupted\"\n\t\t\t// would be false in the second. What is true in both is that something failed and results are gone.\n\t\t\treturn `${readKind} read for '${id}' did not complete; some results are missing and the read reported a failure.`;\n\t\tcase 'page-budget':\n\t\t\treturn `${readKind} read for '${id}' stopped at its page budget; more results can be read by raising it.`;\n\t\tcase 'exhausted':\n\t\t\treturn `${readKind} read for '${id}' was truncated and cannot be continued; results may be incomplete.`;\n\t\tcase 'scope-too-large':\n\t\t\t// Says what is true — the scope is larger than a read of it — rather than blaming the provider for a\n\t\t\t// ceiling that is ours, and names the remedy that exists, since no retry or budget has one.\n\t\t\treturn `${readKind} read for '${id}' covers more than one read can return; narrow the scope to read the rest.`;\n\t}\n\t// No `default`: `IncompleteReadCause` is declared in this file, so `noImplicitReturns` already fails the\n\t// build here if a cause is added without its own wording. (`collectionOmissionMessage`'s `satisfies never`\n\t// is not the same case — that union is the SDK's, and can widen under a dependency bump.)\n}\n\n/**\n * The one sentence every \"you asked for a narrowing this provider can't express\" refusal uses.\n *\n * Shared because the three callers below differ only in a noun: the wording is consumer-visible, and three\n * hand-written copies drift three ways — including the `(supported: …)` clause, which exists to avoid emitting an\n * empty parenthetical and has to be the same guard in all of them.\n */\nfunction unsupportedNarrowingMessage(id: IntegrationIds, noun: string, requested: string, supported: string): string {\n\treturn `The requested ${noun} (${requested}) are not supported by '${id}'${supported.length > 0 ? ` (supported: ${supported})` : ''}; skipped to avoid returning a wider result than requested.`;\n}\n\n/** Warning for an account-wide issue read whose requested filters the provider can't express server-side. */\nexport function unsupportedAccountWideIssueFiltersWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tfilters: IssueFilter[],\n): ProviderWarning {\n\tconst supported = providersMetadata[id]?.supportedAccountWideIssueFilters ?? [];\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\tunsupportedNarrowingMessage(id, 'account-wide issue filters', filters.join(', '), supported.join(', ')),\n\t);\n}\n\n/** Warning for an account-wide PR read whose requested relationship union cannot be expressed exactly. */\nexport function unsupportedAccountWidePullRequestFiltersWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tfilters: PullRequestFilter[],\n): ProviderWarning {\n\tconst supported = providersMetadata[id]?.supportedAccountWidePullRequestFilters ?? [];\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\tunsupportedNarrowingMessage(id, 'account-wide pull request filters', filters.join(', '), supported.join(', ')),\n\t);\n}\n\n/** Warning for a repo-scoped PR read whose requested filters the provider supports none of. */\nexport function unsupportedFiltersWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t`The requested pull request filters are not supported by '${id}'; skipped to avoid returning unfiltered results.`,\n\t);\n}\n\n/**\n * The one sentence every `unusable-scope` refusal uses — the five surfaces that validate a search scope (both\n * searches, both count probes, and issue broadening) differ only in the noun and in whether they name a count\n * key, so the reason a scope is unusable is written once. `prefix` is what the count probes put in front of it\n * to name their key.\n *\n * NAMES the offending values, because which failure mode applied is not deducible from the value: a quote makes\n * `git\"kraken` a request for the real and different org `gitkraken`, a space makes `my org` a search of `my`\n * filtered by the text `org`, and a value of only quotes or whitespace leaves no scope at all. The caller is the\n * only layer that knows which scope it meant.\n *\n * Says how to fix it rather than what was stripped: a consumer cannot mirror this rule (unlike free-text\n * sanitizing, whose rules are published for exactly that), so \"pass the scope as the provider names it\" is the\n * actionable half.\n */\nexport function unusableSearchScopeMessage(noun: string, scopes: readonly string[], prefix?: string): string {\n\treturn `${prefix != null ? `${prefix}: ` : ''}The requested ${noun} (${scopes.map(s => JSON.stringify(s)).join(', ')}) cannot be used as given: a scope name cannot contain quotes, or an inner space or control character, which a query would drop or read as a second qualifier — searching a different scope, or none. Pass the scope exactly as the provider names it.`;\n}\n\n/** Warning for a filtered pull-request search the provider cannot run as requested. */\nexport function unsupportedPullRequestSearchCriteriaWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\trejection: PullRequestSearchCriteriaRejection,\n): ProviderWarning {\n\tlet message: string;\n\tswitch (rejection.reason) {\n\t\tcase 'unsupported-search':\n\t\t\tmessage = `Filtered pull request search is not supported by '${id}'; use \\`listPullRequestsPage\\` for its own pull request reads.`;\n\t\t\tbreak;\n\t\tcase 'unsupported-criteria': {\n\t\t\tconst supported = providersMetadata[id]?.supportedPullRequestSearch;\n\t\t\tmessage = unsupportedNarrowingMessage(\n\t\t\t\tid,\n\t\t\t\t'pull request search criteria',\n\t\t\t\trejection.criteria.join(', '),\n\t\t\t\tsupported != null ? describePullRequestSearchCapabilities(supported) : '',\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn otherWarning(id, domain, connectionId, message);\n}\n\nfunction describePullRequestSearchCapabilities(capabilities: PullRequestSearchCapabilities): string {\n\treturn [\n\t\t...(capabilities.relationships.length ? [`relationships:${capabilities.relationships.join('|')}`] : []),\n\t\t...(capabilities.states.length ? [`states:${capabilities.states.join('|')}`] : []),\n\t\t...(capabilities.text ? ['text'] : []),\n\t\t...(capabilities.updatedAfter ? ['updatedAfter'] : []),\n\t\t...(capabilities.createdAfter ? ['createdAfter'] : []),\n\t\t...(capabilities.includeArchived ? ['includeArchived'] : []),\n\t\t...(capabilities.draft ? ['draft'] : []),\n\t\t...(capabilities.repositoryScope ? ['repository scope'] : []),\n\t\t...(capabilities.organizationScope ? ['organization scope'] : []),\n\t\t...(capabilities.sorts.length ? [`sorts:${capabilities.sorts.join('|')}`] : []),\n\t].join(', ');\n}\n\n/**\n * Warning for a filtered issue search whose criteria the provider can't serve — either it has no such search at\n * all, or it can't express what was asked for, or the set contradicts itself.\n *\n * One builder for all three because they are one decision from the caller's side (\"this search can't be run as\n * asked\") and the remedy differs only in what the message says. The rejection shape carries which case it is, so\n * the wording can't drift from the check that produced it (see {@link resolveIssueSearchCriteria}).\n */\nexport function unsupportedIssueSearchCriteriaWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\trejection: IssueSearchCriteriaRejection,\n): ProviderWarning {\n\tlet message: string;\n\tswitch (rejection.reason) {\n\t\tcase 'unsupported-search':\n\t\t\tmessage = `Filtered issue search is not supported by '${id}'; use \\`listIssuesPage\\` for its own issue reads.`;\n\t\t\tbreak;\n\t\tcase 'unsupported-criteria': {\n\t\t\tconst supported = providersMetadata[id]?.supportedIssueSearch;\n\t\t\tmessage = unsupportedNarrowingMessage(\n\t\t\t\tid,\n\t\t\t\t'issue search criteria',\n\t\t\t\trejection.criteria.join(', '),\n\t\t\t\tsupported != null ? describeIssueSearchCapabilities(supported) : '',\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t\tcase 'contradictory-relationships':\n\t\t\tmessage = `The relationships \\`any-assignee\\` and \\`unassigned\\` are contradictory — they partition the scope between them, so no issue satisfies both; pass only one.`;\n\t\t\tbreak;\n\t}\n\t// No `default`: the union is declared alongside the validator, so `noImplicitReturns` fails the build here if\n\t// a rejection reason is added without its own wording.\n\n\treturn otherWarning(id, domain, connectionId, message);\n}\n\n/** The criteria a provider CAN express, for the \"supported: …\" half of a refusal message. */\nfunction describeIssueSearchCapabilities(capabilities: IssueSearchCapabilities): string {\n\tconst flags: [name: string, supported: boolean][] = [\n\t\t['text', capabilities.text],\n\t\t['labels', capabilities.labels],\n\t\t['milestone', capabilities.milestone],\n\t\t['updatedAfter', capabilities.updatedAfter],\n\t\t['createdAfter', capabilities.createdAfter],\n\t\t['withoutLinkedPullRequest', capabilities.withoutLinkedPullRequest],\n\t\t['state', capabilities.states],\n\t];\n\treturn [\n\t\t...capabilities.relationships.map(r => `relationships:${r}`),\n\t\t...flags.filter(([, supported]) => supported).map(([name]) => name),\n\t\t...capabilities.sorts.map(s => `sort:${s}`),\n\t].join(', ');\n}\n\n/**\n * Warning for an issue read asked to order by a key its provider can't express server-side.\n *\n * Separate from {@link unsupportedIssueSearchCriteriaWarning} because it serves the reads that take `sort` as an\n * OPTION rather than as a criterion — repo-scoped, account-wide, and issue-tracker — where there is no criteria\n * set to fold it into. The filtered search keeps reporting it as one more `unsupported-criteria` entry, so a\n * caller there still gets a single refusal listing everything at once.\n *\n * Takes the rejection the check produced rather than a re-read supported list: which of the two tables applies is\n * the caller's own branch (`repos` present or not), so reading it back here — or threading it by hand — would be\n * free to disagree with the check that refused.\n */\nexport function unsupportedIssueSortWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\trejection: UnsupportedIssueSortRejection,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\tunsupportedNarrowingMessage(id, 'issue sort', rejection.requested, (rejection.supported ?? []).join(', ')),\n\t);\n}\n\n/**\n * Warning for a merged issue read asked to order by a key that no normalized issue carries.\n *\n * The distinction this exists to make is not about the provider: the provider orders by `priority` perfectly well\n * within ONE project. It is about the read, which fans out over several and merges the results here, where the\n * only fields available are the ones {@link IssueShape} models. Serving the concatenated per-scope runs would look\n * ordered without being so, which is why this refuses instead — and why the message says WHY, since the same key\n * against the same provider succeeds on a single-scope read.\n *\n * What a read fans out OVER differs by read, hence the `scope`: repositories, projects, or — where one query is\n * issued per requested relationship and the runs are unioned within a single project (Jira) — the filters\n * themselves. Naming the wrong one would tell a caller to narrow something that isn't what merged.\n */\nexport function unmergeableIssueSortWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\tunmergeable: UnmergeableIssueSort,\n\tscope: 'repositories' | 'projects' | 'filters',\n): ProviderWarning {\n\t// Both forms are spelled out rather than depluralized at runtime: the message needs each, and\n\t// `'repositories'.replace(/s$/, '')` is `'repositorie'`.\n\tconst nouns = {\n\t\trepositories: { many: 'repositories', one: 'repository' },\n\t\tprojects: { many: 'projects', one: 'project' },\n\t\tfilters: { many: 'issue relationships', one: 'relationship' },\n\t}[scope];\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t`Cannot order by '${unmergeable.requested}' across several ${nouns.many}: this read merges their results, and a normalized issue carries no such field to merge on. Read one ${nouns.one} at a time, or order by a date, title, comment or reaction count.`,\n\t);\n}\n\n/**\n * The omission for a filtered issue search that hit the provider's RESULT CEILING: more issues matched than the\n * provider will ever serve for one query, however it is paged.\n *\n * Distinct from {@link truncationWarning} because this is the one incompleteness this read can quantify —\n * `totalCount` is how many matched, `limit` how many are reachable — which is what lets a consumer say \"19.240\n * matched, showing the 1.000 most recently updated\" instead of a bare \"results may be incomplete\". `recovery:\n * 'none'` because the items past the ceiling are UNREACHABLE, not merely unfetched: no budget, no retry, and no\n * cursor would return them. Narrowing the criteria is the only way through, which is a decision for the caller.\n *\n * The message names the ORDER, and has to now that ordering is an option: the reachable window is the top `limit`\n * under that key, so \"the 1.000 most recent\" is a true sentence only while the key is the default one. The same\n * value goes on the omission, so a consumer wording its own sentence doesn't have to remember what it asked for.\n *\n * Returns `undefined` when the provider declares no ceiling, so the caller falls back to the generic wording\n * rather than reporting a limit it invented.\n */\nexport function issueSearchCapResultWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\ttotalCount: number | undefined,\n\tsort: IssueSorting,\n\t/** The provider proved the ceiling was reached without reporting how many matched; see `limitReached`. */\n\tlimitReached?: boolean,\n): ProviderWarning | undefined {\n\treturn searchCapResultWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t'Issue search',\n\t\tprovidersMetadata[id]?.issueSearchResultLimit,\n\t\ttotalCount,\n\t\tsort,\n\t\tlimitReached,\n\t);\n}\n\n/**\n * The shared body of the two ceiling warnings. Only a ceiling that was actually reached warns: either a reported\n * total above it, or a provider that proved the ceiling was reached but can't count past it — in which case the\n * omission carries the limit and the order but no `totalCount`, since any figure would be invented.\n */\nfunction searchCapResultWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\treadKind: 'Issue search' | 'Pull request search',\n\tlimit: number | undefined,\n\ttotalCount: number | undefined,\n\tsort: IssueSorting | PullRequestSorting,\n\tlimitReached: boolean | undefined,\n): ProviderWarning | undefined {\n\tif (limit == null) return undefined;\n\n\t// Below the ceiling this warning would be a false claim; the caller's truncation had another cause.\n\tconst counted = totalCount != null && totalCount > limit;\n\tif (!counted && limitReached !== true) return undefined;\n\n\tconst [field, direction] = sort.split(':');\n\tconst matched = counted ? `matched ${totalCount} results` : `matched more than ${limit} results`;\n\treturn {\n\t\t...otherWarning(\n\t\t\tid,\n\t\t\tdomain,\n\t\t\tconnectionId,\n\t\t\t`${readKind} ${matched}, but '${id}' serves at most ${limit}, ordered by ${field} ${direction}ending; narrow the search to read the rest.`,\n\t\t),\n\t\tomission: {\n\t\t\tkind: 'provider-limit',\n\t\t\t// The items past the ceiling can't be fetched by anything, so never offer a \"load more\".\n\t\t\trecovery: 'none',\n\t\t\tlimit: limit,\n\t\t\t...(counted ? { totalCount: totalCount } : {}),\n\t\t\tsort: sort,\n\t\t},\n\t};\n}\n\n/** Quantified omission for a pull-request search that exceeded the provider's reachable result window. */\nexport function pullRequestSearchCapResultWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n\ttotalCount: number | undefined,\n\tsort: PullRequestSorting,\n\t/** See {@link issueSearchCapResultWarning}. */\n\tlimitReached?: boolean,\n): ProviderWarning | undefined {\n\t// Named order for the same reason as the issue twin: the reachable window is the first `limit` under THIS sort.\n\treturn searchCapResultWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t'Pull request search',\n\t\tprovidersMetadata[id]?.pullRequestSearchResultLimit,\n\t\ttotalCount,\n\t\tsort,\n\t\tlimitReached,\n\t);\n}\n\n/** Warning for a git host that doesn't expose issues on this surface (e.g. Bitbucket, deprecated in favor of Jira). */\nexport function issuesUnsupportedWarning(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tconnectionId: string | undefined,\n): ProviderWarning {\n\treturn otherWarning(\n\t\tid,\n\t\tdomain,\n\t\tconnectionId,\n\t\t`Issues are not supported by '${id}'; use a dedicated issue integration (e.g. Jira) instead.`,\n\t);\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
//#region ../plus/integrations/src/utils/domain.utils.d.ts
|
|
2
2
|
/** Extracts the host from a domain expressed as either a URL or a bare host. */
|
|
3
3
|
export declare function hostFromDomain(domain: string | undefined): string | undefined;
|
|
4
|
+
/** Multiple accounts on the same host are unambiguous; an unknown configured host is not. */
|
|
5
|
+
export declare function getSingleConfiguredDomain(configured: readonly {
|
|
6
|
+
domain?: string;
|
|
7
|
+
}[]): string | undefined;
|
|
4
8
|
/** Whether two URL-or-host domain values identify the same normalized host and port. */
|
|
5
9
|
export declare function areDomainsOnSameHost(first: string | undefined, second: string | undefined): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Whether two URL-or-host domain values name the same machine, ignoring the port. For comparing an SSH remote
|
|
12
|
+
* against a web address: an SSH port says nothing about the port the host serves its web UI and API on.
|
|
13
|
+
*/
|
|
14
|
+
export declare function areDomainsOnSameHostname(first: string | undefined, second: string | undefined): boolean;
|
|
15
|
+
/** Whether a git remote was addressed over the host's web protocol (`https://`/`http://`) rather than SSH/git. */
|
|
16
|
+
export declare function isWebRemoteScheme(scheme: string | undefined): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The comparison to use when matching a git remote's host against a configured (web) host. A web remote names
|
|
19
|
+
* the very host and port the API is served on, so it must match exactly; an SSH or git remote's port — which
|
|
20
|
+
* `parseGitRemoteUrl` drops anyway — says nothing about the web port, so only the hostname can be compared.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getRemoteHostMatcher(scheme: string | undefined): (first: string | undefined, second: string | undefined) => boolean;
|
|
6
23
|
/**
|
|
7
24
|
* Builds the API base URL for a self-managed host from a domain expressed as either a URL or a bare host.
|
|
8
25
|
*
|
|
@@ -22,5 +39,7 @@ export declare function baseUrlFromDomain(domain: string | undefined, protocol:
|
|
|
22
39
|
* connection gaining or losing one.
|
|
23
40
|
*/
|
|
24
41
|
export declare function sameConfiguredBaseUrl(first: string | undefined, second: string | undefined): boolean;
|
|
42
|
+
/** Decodes one percent-encoded URL path segment, keeping a malformed one as written. */
|
|
43
|
+
export declare function decodePathSegment(segment: string): string;
|
|
25
44
|
//#endregion
|
|
26
45
|
//# sourceMappingURL=domain.utils.d.ts.map
|
|
@@ -14,6 +14,11 @@ function hostFromDomain(domain) {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
/** Multiple accounts on the same host are unambiguous; an unknown configured host is not. */
|
|
18
|
+
function getSingleConfiguredDomain(configured) {
|
|
19
|
+
const hosts = new Set(configured.map((connection) => hostFromDomain(connection.domain)));
|
|
20
|
+
return hosts.size === 1 ? hosts.values().next().value : void 0;
|
|
21
|
+
}
|
|
17
22
|
/** Whether two URL-or-host domain values identify the same normalized host and port. */
|
|
18
23
|
function areDomainsOnSameHost(first, second) {
|
|
19
24
|
const firstHost = hostFromDomain(first);
|
|
@@ -21,6 +26,28 @@ function areDomainsOnSameHost(first, second) {
|
|
|
21
26
|
return firstHost != null && secondHost != null && firstHost === secondHost;
|
|
22
27
|
}
|
|
23
28
|
/**
|
|
29
|
+
* Whether two URL-or-host domain values name the same machine, ignoring the port. For comparing an SSH remote
|
|
30
|
+
* against a web address: an SSH port says nothing about the port the host serves its web UI and API on.
|
|
31
|
+
*/
|
|
32
|
+
function areDomainsOnSameHostname(first, second) {
|
|
33
|
+
const firstHost = hostFromDomain(first);
|
|
34
|
+
const secondHost = hostFromDomain(second);
|
|
35
|
+
if (firstHost == null || secondHost == null) return false;
|
|
36
|
+
return new URL(`https://${firstHost}`).hostname === new URL(`https://${secondHost}`).hostname;
|
|
37
|
+
}
|
|
38
|
+
/** Whether a git remote was addressed over the host's web protocol (`https://`/`http://`) rather than SSH/git. */
|
|
39
|
+
function isWebRemoteScheme(scheme) {
|
|
40
|
+
return scheme === "https://" || scheme === "http://";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The comparison to use when matching a git remote's host against a configured (web) host. A web remote names
|
|
44
|
+
* the very host and port the API is served on, so it must match exactly; an SSH or git remote's port — which
|
|
45
|
+
* `parseGitRemoteUrl` drops anyway — says nothing about the web port, so only the hostname can be compared.
|
|
46
|
+
*/
|
|
47
|
+
function getRemoteHostMatcher(scheme) {
|
|
48
|
+
return isWebRemoteScheme(scheme) ? areDomainsOnSameHost : areDomainsOnSameHostname;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
24
51
|
* Builds the API base URL for a self-managed host from a domain expressed as either a URL or a bare host.
|
|
25
52
|
*
|
|
26
53
|
* Distinct from {@link hostFromDomain}, which exists to collapse a domain to the identity the connection is
|
|
@@ -54,7 +81,15 @@ function sameConfiguredBaseUrl(first, second) {
|
|
|
54
81
|
const secondUrl = baseUrlFromDomain(second, void 0);
|
|
55
82
|
return firstUrl != null && secondUrl != null && firstUrl === secondUrl;
|
|
56
83
|
}
|
|
84
|
+
/** Decodes one percent-encoded URL path segment, keeping a malformed one as written. */
|
|
85
|
+
function decodePathSegment(segment) {
|
|
86
|
+
try {
|
|
87
|
+
return decodeURIComponent(segment);
|
|
88
|
+
} catch {
|
|
89
|
+
return segment;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
57
92
|
//#endregion
|
|
58
|
-
export { areDomainsOnSameHost, baseUrlFromDomain, hostFromDomain, sameConfiguredBaseUrl };
|
|
93
|
+
export { areDomainsOnSameHost, areDomainsOnSameHostname, baseUrlFromDomain, decodePathSegment, getRemoteHostMatcher, getSingleConfiguredDomain, hostFromDomain, isWebRemoteScheme, sameConfiguredBaseUrl };
|
|
59
94
|
|
|
60
95
|
//# sourceMappingURL=domain.utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.utils.js","names":[],"sources":["../../../../../plus/integrations/src/utils/domain.utils.ts"],"sourcesContent":["/** Extracts the host from a domain expressed as either a URL or a bare host. */\nexport function hostFromDomain(domain: string | undefined): string | undefined {\n\tconst value = domain?.trim();\n\tif (!value) return undefined;\n\n\tif (/^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(value)) {\n\t\ttry {\n\t\t\treturn new URL(value).host || undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\ttry {\n\t\treturn new URL(`https://${value}`).host || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** Whether two URL-or-host domain values identify the same normalized host and port. */\nexport function areDomainsOnSameHost(first: string | undefined, second: string | undefined): boolean {\n\tconst firstHost = hostFromDomain(first);\n\tconst secondHost = hostFromDomain(second);\n\treturn firstHost != null && secondHost != null && firstHost === secondHost;\n}\n\n/**\n * Builds the API base URL for a self-managed host from a domain expressed as either a URL or a bare host.\n *\n * Distinct from {@link hostFromDomain}, which exists to collapse a domain to the identity the connection is\n * keyed by and therefore DROPS the path. An instance mounted below a context path — Jira Data Center's\n * `/jira`, a reverse-proxied GitLab or Bitbucket — has to be addressed at that path, so the value a request\n * is built from must keep it. Callers pass the session's own domain (the unnormalized one the backend\n * stored) rather than the integration's host-keyed `domain`, and fall back to the latter.\n */\nexport function baseUrlFromDomain(domain: string | undefined, protocol: string | undefined): string | undefined {\n\tconst value = domain?.trim();\n\tif (!value) return undefined;\n\n\tconst scheme = /^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(value) ? undefined : (protocol ?? 'https:');\n\ttry {\n\t\tconst url = new URL(scheme == null ? value : `${scheme}//${value}`);\n\t\treturn `${url.protocol}//${url.host}${url.pathname.replace(/\\/+$/, '')}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Whether two configured addresses point at the same place, comparing them the way a request would.\n *\n * Both sides are run through {@link baseUrlFromDomain} first, so the shapes a domain legitimately arrives in\n * — a bare host, a full URL, a trailing slash — compare equal, while a genuinely different context path does\n * not. Two absent values are the same address (neither names a path); one absent is not, since that is a\n * connection gaining or losing one.\n */\nexport function sameConfiguredBaseUrl(first: string | undefined, second: string | undefined): boolean {\n\tif (!first?.trim() && !second?.trim()) return true;\n\n\tconst firstUrl = baseUrlFromDomain(first, undefined);\n\tconst secondUrl = baseUrlFromDomain(second, undefined);\n\treturn firstUrl != null && secondUrl != null && firstUrl === secondUrl;\n}\n"],"mappings":";;AACA,SAAgB,eAAe,QAAgD;CAC9E,MAAM,QAAQ,QAAQ,KAAK;CAC3B,IAAI,CAAC,OAAO,OAAO,KAAA;CAEnB,IAAI,2BAA2B,KAAK,KAAK,GACxC,IAAI;EACH,OAAO,IAAI,IAAI,KAAK,CAAC,CAAC,QAAQ,KAAA;CAC/B,QAAQ;EACP;CACD;CAGD,IAAI;EACH,OAAO,IAAI,IAAI,WAAW,OAAO,CAAC,CAAC,QAAQ,KAAA;CAC5C,QAAQ;EACP;CACD;AACD;;AAGA,SAAgB,qBAAqB,OAA2B,QAAqC;CACpG,MAAM,YAAY,eAAe,KAAK;CACtC,MAAM,aAAa,eAAe,MAAM;CACxC,OAAO,aAAa,QAAQ,cAAc,QAAQ,cAAc;AACjE;;;;;;;;;;AAWA,SAAgB,kBAAkB,QAA4B,UAAkD;CAC/G,MAAM,QAAQ,QAAQ,KAAK;CAC3B,IAAI,CAAC,OAAO,OAAO,KAAA;CAEnB,MAAM,SAAS,2BAA2B,KAAK,KAAK,IAAI,KAAA,IAAa,YAAY;CACjF,IAAI;EACH,MAAM,MAAM,IAAI,IAAI,UAAU,OAAO,QAAQ,GAAG,OAAO,IAAI,OAAO;EAClE,OAAO,GAAG,IAAI,SAAS,IAAI,IAAI,OAAO,IAAI,SAAS,QAAQ,QAAQ,EAAE;CACtE,QAAQ;EACP;CACD;AACD;;;;;;;;;AAUA,SAAgB,sBAAsB,OAA2B,QAAqC;CACrG,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,QAAQ,KAAK,GAAG,OAAO;CAE9C,MAAM,WAAW,kBAAkB,OAAO,KAAA,CAAS;CACnD,MAAM,YAAY,kBAAkB,QAAQ,KAAA,CAAS;CACrD,OAAO,YAAY,QAAQ,aAAa,QAAQ,aAAa;AAC9D"}
|
|
1
|
+
{"version":3,"file":"domain.utils.js","names":[],"sources":["../../../../../plus/integrations/src/utils/domain.utils.ts"],"sourcesContent":["/** Extracts the host from a domain expressed as either a URL or a bare host. */\nexport function hostFromDomain(domain: string | undefined): string | undefined {\n\tconst value = domain?.trim();\n\tif (!value) return undefined;\n\n\tif (/^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(value)) {\n\t\ttry {\n\t\t\treturn new URL(value).host || undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\ttry {\n\t\treturn new URL(`https://${value}`).host || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** Multiple accounts on the same host are unambiguous; an unknown configured host is not. */\nexport function getSingleConfiguredDomain(configured: readonly { domain?: string }[]): string | undefined {\n\tconst hosts = new Set(configured.map(connection => hostFromDomain(connection.domain)));\n\treturn hosts.size === 1 ? hosts.values().next().value : undefined;\n}\n\n/** Whether two URL-or-host domain values identify the same normalized host and port. */\nexport function areDomainsOnSameHost(first: string | undefined, second: string | undefined): boolean {\n\tconst firstHost = hostFromDomain(first);\n\tconst secondHost = hostFromDomain(second);\n\treturn firstHost != null && secondHost != null && firstHost === secondHost;\n}\n\n/**\n * Whether two URL-or-host domain values name the same machine, ignoring the port. For comparing an SSH remote\n * against a web address: an SSH port says nothing about the port the host serves its web UI and API on.\n */\nexport function areDomainsOnSameHostname(first: string | undefined, second: string | undefined): boolean {\n\tconst firstHost = hostFromDomain(first);\n\tconst secondHost = hostFromDomain(second);\n\tif (firstHost == null || secondHost == null) return false;\n\n\treturn new URL(`https://${firstHost}`).hostname === new URL(`https://${secondHost}`).hostname;\n}\n\n/** Whether a git remote was addressed over the host's web protocol (`https://`/`http://`) rather than SSH/git. */\nexport function isWebRemoteScheme(scheme: string | undefined): boolean {\n\treturn scheme === 'https://' || scheme === 'http://';\n}\n\n/**\n * The comparison to use when matching a git remote's host against a configured (web) host. A web remote names\n * the very host and port the API is served on, so it must match exactly; an SSH or git remote's port — which\n * `parseGitRemoteUrl` drops anyway — says nothing about the web port, so only the hostname can be compared.\n */\nexport function getRemoteHostMatcher(\n\tscheme: string | undefined,\n): (first: string | undefined, second: string | undefined) => boolean {\n\treturn isWebRemoteScheme(scheme) ? areDomainsOnSameHost : areDomainsOnSameHostname;\n}\n\n/**\n * Builds the API base URL for a self-managed host from a domain expressed as either a URL or a bare host.\n *\n * Distinct from {@link hostFromDomain}, which exists to collapse a domain to the identity the connection is\n * keyed by and therefore DROPS the path. An instance mounted below a context path — Jira Data Center's\n * `/jira`, a reverse-proxied GitLab or Bitbucket — has to be addressed at that path, so the value a request\n * is built from must keep it. Callers pass the session's own domain (the unnormalized one the backend\n * stored) rather than the integration's host-keyed `domain`, and fall back to the latter.\n */\nexport function baseUrlFromDomain(domain: string | undefined, protocol: string | undefined): string | undefined {\n\tconst value = domain?.trim();\n\tif (!value) return undefined;\n\n\tconst scheme = /^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(value) ? undefined : (protocol ?? 'https:');\n\ttry {\n\t\tconst url = new URL(scheme == null ? value : `${scheme}//${value}`);\n\t\treturn `${url.protocol}//${url.host}${url.pathname.replace(/\\/+$/, '')}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Whether two configured addresses point at the same place, comparing them the way a request would.\n *\n * Both sides are run through {@link baseUrlFromDomain} first, so the shapes a domain legitimately arrives in\n * — a bare host, a full URL, a trailing slash — compare equal, while a genuinely different context path does\n * not. Two absent values are the same address (neither names a path); one absent is not, since that is a\n * connection gaining or losing one.\n */\nexport function sameConfiguredBaseUrl(first: string | undefined, second: string | undefined): boolean {\n\tif (!first?.trim() && !second?.trim()) return true;\n\n\tconst firstUrl = baseUrlFromDomain(first, undefined);\n\tconst secondUrl = baseUrlFromDomain(second, undefined);\n\treturn firstUrl != null && secondUrl != null && firstUrl === secondUrl;\n}\n\n/** Decodes one percent-encoded URL path segment, keeping a malformed one as written. */\nexport function decodePathSegment(segment: string): string {\n\ttry {\n\t\treturn decodeURIComponent(segment);\n\t} catch {\n\t\treturn segment;\n\t}\n}\n"],"mappings":";;AACA,SAAgB,eAAe,QAAgD;CAC9E,MAAM,QAAQ,QAAQ,KAAK;CAC3B,IAAI,CAAC,OAAO,OAAO,KAAA;CAEnB,IAAI,2BAA2B,KAAK,KAAK,GACxC,IAAI;EACH,OAAO,IAAI,IAAI,KAAK,CAAC,CAAC,QAAQ,KAAA;CAC/B,QAAQ;EACP;CACD;CAGD,IAAI;EACH,OAAO,IAAI,IAAI,WAAW,OAAO,CAAC,CAAC,QAAQ,KAAA;CAC5C,QAAQ;EACP;CACD;AACD;;AAGA,SAAgB,0BAA0B,YAAgE;CACzG,MAAM,QAAQ,IAAI,IAAI,WAAW,KAAI,eAAc,eAAe,WAAW,MAAM,CAAC,CAAC;CACrF,OAAO,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,KAAA;AACzD;;AAGA,SAAgB,qBAAqB,OAA2B,QAAqC;CACpG,MAAM,YAAY,eAAe,KAAK;CACtC,MAAM,aAAa,eAAe,MAAM;CACxC,OAAO,aAAa,QAAQ,cAAc,QAAQ,cAAc;AACjE;;;;;AAMA,SAAgB,yBAAyB,OAA2B,QAAqC;CACxG,MAAM,YAAY,eAAe,KAAK;CACtC,MAAM,aAAa,eAAe,MAAM;CACxC,IAAI,aAAa,QAAQ,cAAc,MAAM,OAAO;CAEpD,OAAO,IAAI,IAAI,WAAW,WAAW,CAAC,CAAC,aAAa,IAAI,IAAI,WAAW,YAAY,CAAC,CAAC;AACtF;;AAGA,SAAgB,kBAAkB,QAAqC;CACtE,OAAO,WAAW,cAAc,WAAW;AAC5C;;;;;;AAOA,SAAgB,qBACf,QACqE;CACrE,OAAO,kBAAkB,MAAM,IAAI,uBAAuB;AAC3D;;;;;;;;;;AAWA,SAAgB,kBAAkB,QAA4B,UAAkD;CAC/G,MAAM,QAAQ,QAAQ,KAAK;CAC3B,IAAI,CAAC,OAAO,OAAO,KAAA;CAEnB,MAAM,SAAS,2BAA2B,KAAK,KAAK,IAAI,KAAA,IAAa,YAAY;CACjF,IAAI;EACH,MAAM,MAAM,IAAI,IAAI,UAAU,OAAO,QAAQ,GAAG,OAAO,IAAI,OAAO;EAClE,OAAO,GAAG,IAAI,SAAS,IAAI,IAAI,OAAO,IAAI,SAAS,QAAQ,QAAQ,EAAE;CACtE,QAAQ;EACP;CACD;AACD;;;;;;;;;AAUA,SAAgB,sBAAsB,OAA2B,QAAqC;CACrG,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,QAAQ,KAAK,GAAG,OAAO;CAE9C,MAAM,WAAW,kBAAkB,OAAO,KAAA,CAAS;CACnD,MAAM,YAAY,kBAAkB,QAAQ,KAAA,CAAS;CACrD,OAAO,YAAY,QAAQ,aAAa,QAAQ,aAAa;AAC9D;;AAGA,SAAgB,kBAAkB,SAAyB;CAC1D,IAAI;EACH,OAAO,mBAAmB,OAAO;CAClC,QAAQ;EACP,OAAO;CACR;AACD"}
|
|
@@ -60,5 +60,6 @@ export declare function warnOnMissingSessionForDomain(id: IntegrationIds, domain
|
|
|
60
60
|
export declare function remoteProviderTypeForIntegration(id: IntegrationIds): RemoteProviderId | undefined;
|
|
61
61
|
/** Normalizes a host remote-config `type` string (e.g. `'GitHub'`) to a git-remote provider type. */
|
|
62
62
|
export declare function remoteProviderTypeForConfig(type: string): RemoteProviderId | undefined;
|
|
63
|
+
export declare function getSelfManagedBaseUrl(id: IntegrationIds, domain: string | undefined, protocol?: string): string | undefined;
|
|
63
64
|
//#endregion
|
|
64
65
|
//# sourceMappingURL=integration.utils.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "../constants.js";
|
|
2
|
+
import { baseUrlFromDomain } from "./domain.utils.js";
|
|
2
3
|
import { isAzureCloudDomain, isBitbucketCloudDomain, isGitHubDotCom, isGitLabDotCom } from "../providers/models.js";
|
|
3
4
|
//#region ../plus/integrations/src/utils/integration.utils.ts
|
|
4
5
|
const selfHostedIntegrationIds = [
|
|
@@ -169,7 +170,17 @@ function remoteProviderTypeForConfig(type) {
|
|
|
169
170
|
default: return;
|
|
170
171
|
}
|
|
171
172
|
}
|
|
173
|
+
function getSelfManagedBaseUrl(id, domain, protocol) {
|
|
174
|
+
const baseUrl = baseUrlFromDomain(domain, protocol);
|
|
175
|
+
if (baseUrl == null) return void 0;
|
|
176
|
+
switch (id) {
|
|
177
|
+
case "cloud-github-enterprise":
|
|
178
|
+
case "cloud-gitlab-self-hosted": return baseUrl.replace(/\/api(?:\/v\d+)?$/, "");
|
|
179
|
+
case "bitbucket-server": return baseUrl.replace(/\/rest\/api\/1\.0$/, "");
|
|
180
|
+
default: return baseUrl;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
172
183
|
//#endregion
|
|
173
|
-
export { convertRemoteProviderIdToIntegrationId, getIntegrationConnectedKey, getIntegrationIdForRemote, isCloudGitSelfManagedHostIntegrationId, isCloudSelfManagedHostIntegrationId, isGitCloudHostIntegrationId, isGitHostIntegration, isGitSelfManagedHostIntegrationId, isIssuesHostIntegrationId, isIssuesSelfManagedHostIntegrationId, isNonExpiringZeroTokenIntegrationId, isSelfManagedHostIntegrationId, remoteProviderTypeForConfig, remoteProviderTypeForIntegration, supportedIntegrationIds, warnOnMissingSessionForDomain };
|
|
184
|
+
export { convertRemoteProviderIdToIntegrationId, getIntegrationConnectedKey, getIntegrationIdForRemote, getSelfManagedBaseUrl, isCloudGitSelfManagedHostIntegrationId, isCloudSelfManagedHostIntegrationId, isGitCloudHostIntegrationId, isGitHostIntegration, isGitSelfManagedHostIntegrationId, isIssuesHostIntegrationId, isIssuesSelfManagedHostIntegrationId, isNonExpiringZeroTokenIntegrationId, isSelfManagedHostIntegrationId, remoteProviderTypeForConfig, remoteProviderTypeForIntegration, supportedIntegrationIds, warnOnMissingSessionForDomain };
|
|
174
185
|
|
|
175
186
|
//# sourceMappingURL=integration.utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.utils.js","names":[],"sources":["../../../../../plus/integrations/src/utils/integration.utils.ts"],"sourcesContent":["import type { RemoteProvider, RemoteProviderId } from '@gitlens/git/models/remoteProvider.js';\nimport type {\n\tCloudGitSelfManagedHostIntegrationIds,\n\tCloudSelfManagedHostIntegrationIds,\n\tIntegrationIds,\n\tIssuesHostIntegrationIds,\n\tSelfManagedHostIntegrationIds,\n} from '../constants.js';\nimport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n\tIssuesSelfManagedHostIntegrationId,\n} from '../constants.js';\nimport type { GitHostIntegration } from '../models/gitHostIntegration.js';\nimport type { Integration, IntegrationConnectedKey } from '../models/integration.js';\n// These domain checks come from `providers/models.ts` (already imported here) rather than the individual\n// provider modules: those read `providersMetadata[...]` synchronously at module init, so importing from them\n// while `providers/models.ts` is still loading triggers a TDZ on `providersMetadata`. Importing the shared\n// predicates keeps one definition of each regex — duplicating them let a fix land in only one place.\nimport { isAzureCloudDomain, isBitbucketCloudDomain, isGitHubDotCom, isGitLabDotCom } from '../providers/models.js';\n\nconst selfHostedIntegrationIds: GitSelfManagedHostIntegrationId[] = [\n\tGitSelfManagedHostIntegrationId.CloudGitHubEnterprise,\n\tGitSelfManagedHostIntegrationId.CloudGitLabSelfHosted,\n\tGitSelfManagedHostIntegrationId.BitbucketServer,\n\tGitSelfManagedHostIntegrationId.AzureDevOpsServer,\n] as const;\n\nconst selfHostedIssuesIntegrationIds: IssuesSelfManagedHostIntegrationId[] = [\n\tIssuesSelfManagedHostIntegrationId.JiraServer,\n] as const;\n\nexport const supportedIntegrationIds: IntegrationIds[] = [\n\tGitCloudHostIntegrationId.GitHub,\n\tGitCloudHostIntegrationId.GitLab,\n\tGitCloudHostIntegrationId.Bitbucket,\n\tGitCloudHostIntegrationId.AzureDevOps,\n\tIssuesCloudHostIntegrationId.Jira,\n\tIssuesCloudHostIntegrationId.Trello,\n\t...selfHostedIntegrationIds,\n\t...selfHostedIssuesIntegrationIds,\n] as const;\n\nexport function convertRemoteProviderIdToIntegrationId(\n\tremoteProviderId: RemoteProviderId,\n): GitCloudHostIntegrationId | GitSelfManagedHostIntegrationId | undefined {\n\tswitch (remoteProviderId) {\n\t\tcase 'azure-devops':\n\t\t\treturn GitCloudHostIntegrationId.AzureDevOps;\n\t\tcase 'bitbucket':\n\t\t\treturn GitCloudHostIntegrationId.Bitbucket;\n\t\tcase 'github':\n\t\t\treturn GitCloudHostIntegrationId.GitHub;\n\t\tcase 'gitlab':\n\t\t\treturn GitCloudHostIntegrationId.GitLab;\n\t\tcase 'bitbucket-server':\n\t\t\treturn GitSelfManagedHostIntegrationId.BitbucketServer;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function getIntegrationConnectedKey<T extends IntegrationIds>(\n\tid: T,\n\tdomain?: string,\n): IntegrationConnectedKey<T> {\n\tif (isSelfManagedHostIntegrationId(id)) {\n\t\tif (!domain) {\n\t\t\tthrow new Error(`Domain is required for self-managed integration ID: ${id}`);\n\t\t}\n\t\treturn `connected:${id}:${domain}` as IntegrationConnectedKey<T>;\n\t}\n\n\treturn `connected:${id}` as IntegrationConnectedKey<T>;\n}\n\nexport function getIntegrationIdForRemote(\n\tprovider: RemoteProvider | undefined,\n): GitCloudHostIntegrationId | GitSelfManagedHostIntegrationId | undefined {\n\tswitch (provider?.id) {\n\t\tcase 'azure-devops':\n\t\t\tif (isAzureCloudDomain(provider.domain)) {\n\t\t\t\treturn GitCloudHostIntegrationId.AzureDevOps;\n\t\t\t}\n\t\t\treturn provider.custom ? undefined : GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\t\tcase 'bitbucket':\n\t\tcase 'bitbucket-server':\n\t\t\tif (isBitbucketCloudDomain(provider.domain)) {\n\t\t\t\treturn GitCloudHostIntegrationId.Bitbucket;\n\t\t\t}\n\t\t\treturn GitSelfManagedHostIntegrationId.BitbucketServer;\n\t\tcase 'github':\n\t\t\tif (provider.domain != null && !isGitHubDotCom(provider.domain)) {\n\t\t\t\treturn GitSelfManagedHostIntegrationId.CloudGitHubEnterprise;\n\t\t\t}\n\t\t\treturn GitCloudHostIntegrationId.GitHub;\n\t\tcase 'gitlab':\n\t\t\tif (provider.domain != null && !isGitLabDotCom(provider.domain)) {\n\t\t\t\treturn GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted;\n\t\t\t}\n\t\t\treturn GitCloudHostIntegrationId.GitLab;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function isCloudGitSelfManagedHostIntegrationId(\n\tid: IntegrationIds,\n): id is CloudGitSelfManagedHostIntegrationIds {\n\tswitch (id) {\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitHubEnterprise:\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\tcase GitSelfManagedHostIntegrationId.BitbucketServer:\n\t\tcase GitSelfManagedHostIntegrationId.AzureDevOpsServer:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Whether this id is a self-managed host whose token comes from the GK cloud backend — the check behind\n * \"expand this provider to one instance per configured host\". Distinct from\n * {@link isCloudGitSelfManagedHostIntegrationId}, which stays git-only because its callers go on to ask the\n * instance for repositories.\n */\nexport function isCloudSelfManagedHostIntegrationId(id: IntegrationIds): id is CloudSelfManagedHostIntegrationIds {\n\treturn isCloudGitSelfManagedHostIntegrationId(id) || isIssuesSelfManagedHostIntegrationId(id);\n}\n\n/**\n * Whether a provider's cloud token uses `expiresIn: 0` to mean \"never expires\" (rather than \"already\n * expired\"). GitHub and the cloud self-managed hosts always return 0 for their non-expiring tokens; Trello\n * is issued with `expiration: never` (identity-service), so its cloud token comes back as 0 too. Callers\n * must map 0 → a far-future expiry for these, or the session is immediately treated as expired.\n *\n * Every backend-managed self-managed host qualifies, not just the git ones: a self-hosted tracker's token is\n * a PAT the backend stores as-is and cannot refresh, so reading 0 as \"already expired\" would re-resolve the\n * session on every single read.\n */\nexport function isNonExpiringZeroTokenIntegrationId(id: IntegrationIds): boolean {\n\treturn (\n\t\tid === GitCloudHostIntegrationId.GitHub ||\n\t\tid === IssuesCloudHostIntegrationId.Trello ||\n\t\tisCloudSelfManagedHostIntegrationId(id)\n\t);\n}\n\nexport function isGitHostIntegration(integration: Integration): integration is GitHostIntegration {\n\treturn integration.type === 'git';\n}\n\nexport function isGitCloudHostIntegrationId(id: IntegrationIds): id is GitCloudHostIntegrationId {\n\tswitch (id) {\n\t\tcase GitCloudHostIntegrationId.GitHub:\n\t\tcase GitCloudHostIntegrationId.GitLab:\n\t\tcase GitCloudHostIntegrationId.Bitbucket:\n\t\tcase GitCloudHostIntegrationId.AzureDevOps:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nexport function isGitSelfManagedHostIntegrationId(id: IntegrationIds): id is GitSelfManagedHostIntegrationId {\n\treturn selfHostedIntegrationIds.includes(id as GitSelfManagedHostIntegrationId);\n}\n\nexport function isIssuesSelfManagedHostIntegrationId(id: IntegrationIds): id is IssuesSelfManagedHostIntegrationId {\n\treturn selfHostedIssuesIntegrationIds.includes(id as IssuesSelfManagedHostIntegrationId);\n}\n\n/**\n * Whether this id addresses ONE customer-run host, whatever it hosts — the predicate the domain-keyed\n * machinery runs on: the integration cache key, the secret key, the `connected:${id}:${domain}` flag, domain\n * normalization, per-host primary selection, and multi-account reconcile.\n *\n * Deliberately wider than {@link isGitSelfManagedHostIntegrationId}, which is reserved for reads that go on to\n * ask for repositories, pull requests, or a git remote. Widening THOSE to a tracker would attribute a Jira\n * host's remotes to Jira; narrowing THESE to git hosts would key a tracker's connections under an empty\n * domain, collapsing every host of it into one bucket.\n */\nexport function isSelfManagedHostIntegrationId(id: IntegrationIds): id is SelfManagedHostIntegrationIds {\n\treturn isGitSelfManagedHostIntegrationId(id) || isIssuesSelfManagedHostIntegrationId(id);\n}\n\n/**\n * Whether this id belongs to a dedicated issue tracker (resource → project) rather than a git host.\n *\n * Decided from the id alone, so a read can refuse a mismatched surface — a repo/PR read asked of Jira, or an\n * issue-tracker project read asked of GitHub — before resolving a connection. The instance-level\n * {@link isIssuesIntegration} answers the same question once an integration is in hand.\n */\nexport function isIssuesHostIntegrationId(id: IntegrationIds): id is IssuesHostIntegrationIds {\n\tswitch (id) {\n\t\tcase IssuesCloudHostIntegrationId.Jira:\n\t\tcase IssuesCloudHostIntegrationId.Linear:\n\t\tcase IssuesCloudHostIntegrationId.Trello:\n\t\tcase IssuesSelfManagedHostIntegrationId.JiraServer:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Whether a read targeted only by an explicit self-managed `domain` (no `connectionId`) must treat a\n * session-less core result as a broken target instead of an empty account. Without this, a self-managed host\n * addressed only by domain — the manual-token/external-auth case `domain` exists to cover — returns an empty\n * success with no warning and no `fetchFailed`, indistinguishable from \"this host has nothing\".\n */\nexport function warnOnMissingSessionForDomain(id: IntegrationIds, domain: string | undefined): boolean {\n\treturn domain != null && isSelfManagedHostIntegrationId(id);\n}\n\n/** Maps an integration id to the git-remote provider type used by the remote-URL matcher. */\nexport function remoteProviderTypeForIntegration(id: IntegrationIds): RemoteProviderId | undefined {\n\tswitch (id) {\n\t\tcase GitCloudHostIntegrationId.GitHub:\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitHubEnterprise:\n\t\t\treturn 'github';\n\t\tcase GitCloudHostIntegrationId.GitLab:\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\t\treturn 'gitlab';\n\t\tcase GitCloudHostIntegrationId.Bitbucket:\n\t\t\treturn 'bitbucket';\n\t\tcase GitSelfManagedHostIntegrationId.BitbucketServer:\n\t\t\treturn 'bitbucket-server';\n\t\tcase GitCloudHostIntegrationId.AzureDevOps:\n\t\tcase GitSelfManagedHostIntegrationId.AzureDevOpsServer:\n\t\t\treturn 'azure-devops';\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/** Normalizes a host remote-config `type` string (e.g. `'GitHub'`) to a git-remote provider type. */\nexport function remoteProviderTypeForConfig(type: string): RemoteProviderId | undefined {\n\tswitch (type.toLowerCase()) {\n\t\tcase 'github':\n\t\t\treturn 'github';\n\t\tcase 'gitlab':\n\t\t\treturn 'gitlab';\n\t\tcase 'bitbucket':\n\t\t\treturn 'bitbucket';\n\t\tcase 'bitbucket-server':\n\t\tcase 'bitbucketserver':\n\t\t\treturn 'bitbucket-server';\n\t\tcase 'azuredevops':\n\t\tcase 'azure-devops':\n\t\t\treturn 'azure-devops';\n\t\tcase 'gitea':\n\t\t\treturn 'gitea';\n\t\tcase 'gerrit':\n\t\t\treturn 'gerrit';\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n"],"mappings":";;;AAsBA,MAAM,2BAA8D;;;;;AAKpE;AAEA,MAAM,iCAAuE,CAAA,aAE7E;AAEA,MAAa,0BAA4C;;;;;;;CAOxD,GAAG;CACH,GAAG;AACJ;AAEA,SAAgB,uCACf,kBAC0E;CAC1E,QAAQ,kBAAR;EACC,KAAK,gBACJ,OAAA;EACD,KAAK,aACJ,OAAA;EACD,KAAK,UACJ,OAAA;EACD,KAAK,UACJ,OAAA;EACD,KAAK,oBACJ,OAAA;EACD,SACC;CACF;AACD;AAEA,SAAgB,2BACf,IACA,QAC6B;CAC7B,IAAI,+BAA+B,EAAE,GAAG;EACvC,IAAI,CAAC,QACJ,MAAM,IAAI,MAAM,uDAAuD,IAAI;EAE5E,OAAO,aAAa,GAAG,GAAG;CAC3B;CAEA,OAAO,aAAa;AACrB;AAEA,SAAgB,0BACf,UAC0E;CAC1E,QAAQ,UAAU,IAAlB;EACC,KAAK;GACJ,IAAI,mBAAmB,SAAS,MAAM,GACrC,OAAA;GAED,OAAO,SAAS,SAAS,KAAA,IAAA;EAC1B,KAAK;EACL,KAAK;GACJ,IAAI,uBAAuB,SAAS,MAAM,GACzC,OAAA;GAED,OAAA;EACD,KAAK;GACJ,IAAI,SAAS,UAAU,QAAQ,CAAC,eAAe,SAAS,MAAM,GAC7D,OAAA;GAED,OAAA;EACD,KAAK;GACJ,IAAI,SAAS,UAAU,QAAQ,CAAC,eAAe,SAAS,MAAM,GAC7D,OAAA;GAED,OAAA;EACD,SACC;CACF;AACD;AAEA,SAAgB,uCACf,IAC8C;CAC9C,QAAQ,IAAR;EACC,KAAA;EACA,KAAA;EACA,KAAA;EACA,KAAA,uBACC,OAAO;EACR,SACC,OAAO;CACT;AACD;;;;;;;AAQA,SAAgB,oCAAoC,IAA8D;CACjH,OAAO,uCAAuC,EAAE,KAAK,qCAAqC,EAAE;AAC7F;;;;;;;;;;;AAYA,SAAgB,oCAAoC,IAA6B;CAChF,OACC,OAAA,YACA,OAAA,YACA,oCAAoC,EAAE;AAExC;AAEA,SAAgB,qBAAqB,aAA6D;CACjG,OAAO,YAAY,SAAS;AAC7B;AAEA,SAAgB,4BAA4B,IAAqD;CAChG,QAAQ,IAAR;EACC,KAAA;EACA,KAAA;EACA,KAAA;EACA,KAAA,eACC,OAAO;EACR,SACC,OAAO;CACT;AACD;AAEA,SAAgB,kCAAkC,IAA2D;CAC5G,OAAO,yBAAyB,SAAS,EAAqC;AAC/E;AAEA,SAAgB,qCAAqC,IAA8D;CAClH,OAAO,+BAA+B,SAAS,EAAwC;AACxF;;;;;;;;;;;AAYA,SAAgB,+BAA+B,IAAyD;CACvG,OAAO,kCAAkC,EAAE,KAAK,qCAAqC,EAAE;AACxF;;;;;;;;AASA,SAAgB,0BAA0B,IAAoD;CAC7F,QAAQ,IAAR;EACC,KAAA;EACA,KAAA;EACA,KAAA;EACA,KAAA,eACC,OAAO;EACR,SACC,OAAO;CACT;AACD;;;;;;;AAQA,SAAgB,8BAA8B,IAAoB,QAAqC;CACtG,OAAO,UAAU,QAAQ,+BAA+B,EAAE;AAC3D;;AAGA,SAAgB,iCAAiC,IAAkD;CAClG,QAAQ,IAAR;EACC,KAAA;EACA,KAAA,2BACC,OAAO;EACR,KAAA;EACA,KAAA,4BACC,OAAO;EACR,KAAA,aACC,OAAO;EACR,KAAA,oBACC,OAAO;EACR,KAAA;EACA,KAAA,uBACC,OAAO;EACR,SACC;CACF;AACD;;AAGA,SAAgB,4BAA4B,MAA4C;CACvF,QAAQ,KAAK,YAAY,GAAzB;EACC,KAAK,UACJ,OAAO;EACR,KAAK,UACJ,OAAO;EACR,KAAK,aACJ,OAAO;EACR,KAAK;EACL,KAAK,mBACJ,OAAO;EACR,KAAK;EACL,KAAK,gBACJ,OAAO;EACR,KAAK,SACJ,OAAO;EACR,KAAK,UACJ,OAAO;EACR,SACC;CACF;AACD"}
|
|
1
|
+
{"version":3,"file":"integration.utils.js","names":[],"sources":["../../../../../plus/integrations/src/utils/integration.utils.ts"],"sourcesContent":["import type { RemoteProvider, RemoteProviderId } from '@gitlens/git/models/remoteProvider.js';\nimport type {\n\tCloudGitSelfManagedHostIntegrationIds,\n\tCloudSelfManagedHostIntegrationIds,\n\tIntegrationIds,\n\tIssuesHostIntegrationIds,\n\tSelfManagedHostIntegrationIds,\n} from '../constants.js';\nimport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n\tIssuesSelfManagedHostIntegrationId,\n} from '../constants.js';\nimport type { GitHostIntegration } from '../models/gitHostIntegration.js';\nimport type { Integration, IntegrationConnectedKey } from '../models/integration.js';\n// These domain checks come from `providers/models.ts` (already imported here) rather than the individual\n// provider modules: those read `providersMetadata[...]` synchronously at module init, so importing from them\n// while `providers/models.ts` is still loading triggers a TDZ on `providersMetadata`. Importing the shared\n// predicates keeps one definition of each regex — duplicating them let a fix land in only one place.\nimport { isAzureCloudDomain, isBitbucketCloudDomain, isGitHubDotCom, isGitLabDotCom } from '../providers/models.js';\nimport { baseUrlFromDomain } from './domain.utils.js';\n\nconst selfHostedIntegrationIds: GitSelfManagedHostIntegrationId[] = [\n\tGitSelfManagedHostIntegrationId.CloudGitHubEnterprise,\n\tGitSelfManagedHostIntegrationId.CloudGitLabSelfHosted,\n\tGitSelfManagedHostIntegrationId.BitbucketServer,\n\tGitSelfManagedHostIntegrationId.AzureDevOpsServer,\n] as const;\n\nconst selfHostedIssuesIntegrationIds: IssuesSelfManagedHostIntegrationId[] = [\n\tIssuesSelfManagedHostIntegrationId.JiraServer,\n] as const;\n\nexport const supportedIntegrationIds: IntegrationIds[] = [\n\tGitCloudHostIntegrationId.GitHub,\n\tGitCloudHostIntegrationId.GitLab,\n\tGitCloudHostIntegrationId.Bitbucket,\n\tGitCloudHostIntegrationId.AzureDevOps,\n\tIssuesCloudHostIntegrationId.Jira,\n\tIssuesCloudHostIntegrationId.Trello,\n\t...selfHostedIntegrationIds,\n\t...selfHostedIssuesIntegrationIds,\n] as const;\n\nexport function convertRemoteProviderIdToIntegrationId(\n\tremoteProviderId: RemoteProviderId,\n): GitCloudHostIntegrationId | GitSelfManagedHostIntegrationId | undefined {\n\tswitch (remoteProviderId) {\n\t\tcase 'azure-devops':\n\t\t\treturn GitCloudHostIntegrationId.AzureDevOps;\n\t\tcase 'bitbucket':\n\t\t\treturn GitCloudHostIntegrationId.Bitbucket;\n\t\tcase 'github':\n\t\t\treturn GitCloudHostIntegrationId.GitHub;\n\t\tcase 'gitlab':\n\t\t\treturn GitCloudHostIntegrationId.GitLab;\n\t\tcase 'bitbucket-server':\n\t\t\treturn GitSelfManagedHostIntegrationId.BitbucketServer;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function getIntegrationConnectedKey<T extends IntegrationIds>(\n\tid: T,\n\tdomain?: string,\n): IntegrationConnectedKey<T> {\n\tif (isSelfManagedHostIntegrationId(id)) {\n\t\tif (!domain) {\n\t\t\tthrow new Error(`Domain is required for self-managed integration ID: ${id}`);\n\t\t}\n\t\treturn `connected:${id}:${domain}` as IntegrationConnectedKey<T>;\n\t}\n\n\treturn `connected:${id}` as IntegrationConnectedKey<T>;\n}\n\nexport function getIntegrationIdForRemote(\n\tprovider: RemoteProvider | undefined,\n): GitCloudHostIntegrationId | GitSelfManagedHostIntegrationId | undefined {\n\tswitch (provider?.id) {\n\t\tcase 'azure-devops':\n\t\t\tif (isAzureCloudDomain(provider.domain)) {\n\t\t\t\treturn GitCloudHostIntegrationId.AzureDevOps;\n\t\t\t}\n\t\t\treturn provider.custom ? undefined : GitSelfManagedHostIntegrationId.AzureDevOpsServer;\n\t\tcase 'bitbucket':\n\t\tcase 'bitbucket-server':\n\t\t\tif (isBitbucketCloudDomain(provider.domain)) {\n\t\t\t\treturn GitCloudHostIntegrationId.Bitbucket;\n\t\t\t}\n\t\t\treturn GitSelfManagedHostIntegrationId.BitbucketServer;\n\t\tcase 'github':\n\t\t\tif (provider.domain != null && !isGitHubDotCom(provider.domain)) {\n\t\t\t\treturn GitSelfManagedHostIntegrationId.CloudGitHubEnterprise;\n\t\t\t}\n\t\t\treturn GitCloudHostIntegrationId.GitHub;\n\t\tcase 'gitlab':\n\t\t\tif (provider.domain != null && !isGitLabDotCom(provider.domain)) {\n\t\t\t\treturn GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted;\n\t\t\t}\n\t\t\treturn GitCloudHostIntegrationId.GitLab;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function isCloudGitSelfManagedHostIntegrationId(\n\tid: IntegrationIds,\n): id is CloudGitSelfManagedHostIntegrationIds {\n\tswitch (id) {\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitHubEnterprise:\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\tcase GitSelfManagedHostIntegrationId.BitbucketServer:\n\t\tcase GitSelfManagedHostIntegrationId.AzureDevOpsServer:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Whether this id is a self-managed host whose token comes from the GK cloud backend — the check behind\n * \"expand this provider to one instance per configured host\". Distinct from\n * {@link isCloudGitSelfManagedHostIntegrationId}, which stays git-only because its callers go on to ask the\n * instance for repositories.\n */\nexport function isCloudSelfManagedHostIntegrationId(id: IntegrationIds): id is CloudSelfManagedHostIntegrationIds {\n\treturn isCloudGitSelfManagedHostIntegrationId(id) || isIssuesSelfManagedHostIntegrationId(id);\n}\n\n/**\n * Whether a provider's cloud token uses `expiresIn: 0` to mean \"never expires\" (rather than \"already\n * expired\"). GitHub and the cloud self-managed hosts always return 0 for their non-expiring tokens; Trello\n * is issued with `expiration: never` (identity-service), so its cloud token comes back as 0 too. Callers\n * must map 0 → a far-future expiry for these, or the session is immediately treated as expired.\n *\n * Every backend-managed self-managed host qualifies, not just the git ones: a self-hosted tracker's token is\n * a PAT the backend stores as-is and cannot refresh, so reading 0 as \"already expired\" would re-resolve the\n * session on every single read.\n */\nexport function isNonExpiringZeroTokenIntegrationId(id: IntegrationIds): boolean {\n\treturn (\n\t\tid === GitCloudHostIntegrationId.GitHub ||\n\t\tid === IssuesCloudHostIntegrationId.Trello ||\n\t\tisCloudSelfManagedHostIntegrationId(id)\n\t);\n}\n\nexport function isGitHostIntegration(integration: Integration): integration is GitHostIntegration {\n\treturn integration.type === 'git';\n}\n\nexport function isGitCloudHostIntegrationId(id: IntegrationIds): id is GitCloudHostIntegrationId {\n\tswitch (id) {\n\t\tcase GitCloudHostIntegrationId.GitHub:\n\t\tcase GitCloudHostIntegrationId.GitLab:\n\t\tcase GitCloudHostIntegrationId.Bitbucket:\n\t\tcase GitCloudHostIntegrationId.AzureDevOps:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nexport function isGitSelfManagedHostIntegrationId(id: IntegrationIds): id is GitSelfManagedHostIntegrationId {\n\treturn selfHostedIntegrationIds.includes(id as GitSelfManagedHostIntegrationId);\n}\n\nexport function isIssuesSelfManagedHostIntegrationId(id: IntegrationIds): id is IssuesSelfManagedHostIntegrationId {\n\treturn selfHostedIssuesIntegrationIds.includes(id as IssuesSelfManagedHostIntegrationId);\n}\n\n/**\n * Whether this id addresses ONE customer-run host, whatever it hosts — the predicate the domain-keyed\n * machinery runs on: the integration cache key, the secret key, the `connected:${id}:${domain}` flag, domain\n * normalization, per-host primary selection, and multi-account reconcile.\n *\n * Deliberately wider than {@link isGitSelfManagedHostIntegrationId}, which is reserved for reads that go on to\n * ask for repositories, pull requests, or a git remote. Widening THOSE to a tracker would attribute a Jira\n * host's remotes to Jira; narrowing THESE to git hosts would key a tracker's connections under an empty\n * domain, collapsing every host of it into one bucket.\n */\nexport function isSelfManagedHostIntegrationId(id: IntegrationIds): id is SelfManagedHostIntegrationIds {\n\treturn isGitSelfManagedHostIntegrationId(id) || isIssuesSelfManagedHostIntegrationId(id);\n}\n\n/**\n * Whether this id belongs to a dedicated issue tracker (resource → project) rather than a git host.\n *\n * Decided from the id alone, so a read can refuse a mismatched surface — a repo/PR read asked of Jira, or an\n * issue-tracker project read asked of GitHub — before resolving a connection. The instance-level\n * {@link isIssuesIntegration} answers the same question once an integration is in hand.\n */\nexport function isIssuesHostIntegrationId(id: IntegrationIds): id is IssuesHostIntegrationIds {\n\tswitch (id) {\n\t\tcase IssuesCloudHostIntegrationId.Jira:\n\t\tcase IssuesCloudHostIntegrationId.Linear:\n\t\tcase IssuesCloudHostIntegrationId.Trello:\n\t\tcase IssuesSelfManagedHostIntegrationId.JiraServer:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Whether a read targeted only by an explicit self-managed `domain` (no `connectionId`) must treat a\n * session-less core result as a broken target instead of an empty account. Without this, a self-managed host\n * addressed only by domain — the manual-token/external-auth case `domain` exists to cover — returns an empty\n * success with no warning and no `fetchFailed`, indistinguishable from \"this host has nothing\".\n */\nexport function warnOnMissingSessionForDomain(id: IntegrationIds, domain: string | undefined): boolean {\n\treturn domain != null && isSelfManagedHostIntegrationId(id);\n}\n\n/** Maps an integration id to the git-remote provider type used by the remote-URL matcher. */\nexport function remoteProviderTypeForIntegration(id: IntegrationIds): RemoteProviderId | undefined {\n\tswitch (id) {\n\t\tcase GitCloudHostIntegrationId.GitHub:\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitHubEnterprise:\n\t\t\treturn 'github';\n\t\tcase GitCloudHostIntegrationId.GitLab:\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\t\treturn 'gitlab';\n\t\tcase GitCloudHostIntegrationId.Bitbucket:\n\t\t\treturn 'bitbucket';\n\t\tcase GitSelfManagedHostIntegrationId.BitbucketServer:\n\t\t\treturn 'bitbucket-server';\n\t\tcase GitCloudHostIntegrationId.AzureDevOps:\n\t\tcase GitSelfManagedHostIntegrationId.AzureDevOpsServer:\n\t\t\treturn 'azure-devops';\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/** Normalizes a host remote-config `type` string (e.g. `'GitHub'`) to a git-remote provider type. */\nexport function remoteProviderTypeForConfig(type: string): RemoteProviderId | undefined {\n\tswitch (type.toLowerCase()) {\n\t\tcase 'github':\n\t\t\treturn 'github';\n\t\tcase 'gitlab':\n\t\t\treturn 'gitlab';\n\t\tcase 'bitbucket':\n\t\t\treturn 'bitbucket';\n\t\tcase 'bitbucket-server':\n\t\tcase 'bitbucketserver':\n\t\t\treturn 'bitbucket-server';\n\t\tcase 'azuredevops':\n\t\tcase 'azure-devops':\n\t\t\treturn 'azure-devops';\n\t\tcase 'gitea':\n\t\t\treturn 'gitea';\n\t\tcase 'gerrit':\n\t\t\treturn 'gerrit';\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function getSelfManagedBaseUrl(\n\tid: IntegrationIds,\n\tdomain: string | undefined,\n\tprotocol?: string,\n): string | undefined {\n\tconst baseUrl = baseUrlFromDomain(domain, protocol);\n\tif (baseUrl == null) return undefined;\n\n\tswitch (id) {\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitHubEnterprise:\n\t\tcase GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted:\n\t\t\treturn baseUrl.replace(/\\/api(?:\\/v\\d+)?$/, '');\n\t\tcase GitSelfManagedHostIntegrationId.BitbucketServer:\n\t\t\treturn baseUrl.replace(/\\/rest\\/api\\/1\\.0$/, '');\n\t\tdefault:\n\t\t\treturn baseUrl;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -2,10 +2,10 @@ import { getScopedLogger, maybeStartScopedLogger } from "../../logger.scoped.js"
|
|
|
2
2
|
import { normalizePath } from "../../path.js";
|
|
3
3
|
import { isWindows } from "./platform.js";
|
|
4
4
|
import { join } from "path";
|
|
5
|
-
import { exec, execFile, spawn } from "child_process";
|
|
6
|
-
import { access, constants } from "fs";
|
|
7
5
|
import { stat } from "fs/promises";
|
|
8
6
|
import * as process from "process";
|
|
7
|
+
import { exec, execFile, spawn } from "child_process";
|
|
8
|
+
import { access, constants } from "fs";
|
|
9
9
|
//#region ../utils/src/env/node/exec.ts
|
|
10
10
|
var RunError = class extends Error {
|
|
11
11
|
cmd;
|