@gitkraken/core-gitlens 0.21.0 → 0.22.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 +17 -1
- package/dist/git/utils/autolink.utils.d.ts +4 -1
- package/dist/git/utils/autolink.utils.js +12 -3
- package/dist/git/utils/autolink.utils.js.map +1 -1
- package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts +7 -0
- package/dist/plus/integrations/authentication/configuredIntegrationService.js +12 -0
- package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationService.js +5 -3
- package/dist/plus/integrations/authentication/integrationAuthenticationService.js.map +1 -1
- package/dist/plus/integrations/authentication/models.d.ts +9 -0
- package/dist/plus/integrations/authentication/models.js +13 -1
- package/dist/plus/integrations/authentication/models.js.map +1 -1
- package/dist/plus/integrations/authentication/observedAuthenticationProvider.d.ts +34 -0
- package/dist/plus/integrations/authentication/observedAuthenticationProvider.js +227 -0
- package/dist/plus/integrations/authentication/observedAuthenticationProvider.js.map +1 -0
- package/dist/plus/integrations/index.d.ts +4 -2
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +9 -4
- package/dist/plus/integrations/integrationService.js +30 -7
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +18 -1
- package/dist/plus/integrations/models/integration.d.ts +8 -1
- package/dist/plus/integrations/models/integration.js +79 -10
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issuesIntegration.d.ts +20 -1
- package/dist/plus/integrations/models/issuesIntegration.js +106 -2
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
- package/dist/plus/integrations/providers/jira-server.d.ts +10 -2
- package/dist/plus/integrations/providers/jira-server.js +74 -7
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +3 -3
- package/dist/plus/integrations/providers/jira.js +5 -27
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/linear.d.ts +1 -1
- package/dist/plus/integrations/providers/linear.js +3 -27
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/providerApiClient.js +4 -1
- package/dist/plus/integrations/providers/providerApiClient.js.map +1 -1
- package/dist/plus/integrations/providers/providerErrors.d.ts +4 -1
- package/dist/plus/integrations/providers/providerErrors.js +13 -4
- package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.js +1 -1
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/trello.d.ts +1 -1
- package/dist/plus/integrations/providers/trello.js +1 -1
- package/dist/plus/integrations/providers/trello.js.map +1 -1
- package/dist/plus/integrations/providers/utils/issueAutolinks.d.ts +8 -0
- package/dist/plus/integrations/providers/utils/issueAutolinks.js +20 -0
- package/dist/plus/integrations/providers/utils/issueAutolinks.js.map +1 -0
- package/dist/plus/integrations/reads/autolinks.d.ts +7 -0
- package/dist/plus/integrations/reads/autolinks.js +40 -0
- package/dist/plus/integrations/reads/autolinks.js.map +1 -0
- package/dist/plus/integrations/reads/context.d.ts +4 -2
- package/dist/plus/integrations/reads/drains.d.ts +0 -1
- package/dist/plus/integrations/reads/hierarchy.js +1 -1
- package/dist/plus/integrations/reads/issueBatch.js +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +1 -1
- package/dist/plus/integrations/reads/resolveRepository.js +1 -1
- package/docs/integrations.md +22 -1
- package/package.json +1 -1
|
@@ -30,7 +30,7 @@ export declare class TrelloIntegration extends IssuesIntegration<IssuesCloudHost
|
|
|
30
30
|
protected getProviderCurrentAccount(session: ProviderAuthenticationSession): Promise<Account | undefined>;
|
|
31
31
|
protected getProviderUncachedAccountForResource(session: ProviderAuthenticationSession, _resource: ResourceDescriptor): Promise<Account | undefined>;
|
|
32
32
|
protected getProviderAccountForResource(session: ProviderAuthenticationSession, _resource: ResourceDescriptor): Promise<Account | undefined>;
|
|
33
|
-
|
|
33
|
+
protected clearDiscoveryCaches(): void;
|
|
34
34
|
private readonly _recentAccounts;
|
|
35
35
|
private fetchAccount;
|
|
36
36
|
protected getProviderResourcesForUser(session: ProviderAuthenticationSession): Promise<TrelloBoardDescriptor[] | undefined>;
|
|
@@ -56,7 +56,7 @@ var TrelloIntegration = class extends IssuesIntegration {
|
|
|
56
56
|
const appKey = this.requireAppKey(session);
|
|
57
57
|
return this._recentAccounts.getOrCreate(session.accessToken, () => this.fetchAccount(session, appKey), { evictWhen: (account) => account == null });
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
clearDiscoveryCaches() {
|
|
60
60
|
this._recentAccounts.clear();
|
|
61
61
|
}
|
|
62
62
|
_recentAccounts = new PromiseCache({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trello.js","names":[],"sources":["../../../../../plus/integrations/src/providers/trello.ts"],"sourcesContent":["import type { Account } from '@gitlens/git/models/author.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestType } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { IssueResourceDescriptor, ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { isIssueResourceDescriptor } from '@gitlens/git/utils/resourceDescriptor.utils.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { IssuesCloudHostIntegrationId } from '../constants.js';\nimport { IntegrationReadUnavailableError } from '../errors.js';\nimport type { IssuesForProjectOptions, ProjectIssuesDrain } from '../models/issueReads.js';\nimport { IssuesIntegration } from '../models/issuesIntegration.js';\nimport { fromProviderIssue, providersMetadata, toIssueShape } from './models.js';\n\nconst metadata = providersMetadata[IssuesCloudHostIntegrationId.Trello];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\n/** Long enough to span one tracker read, short enough that a profile change shows on the next. */\nconst recentAccountTTL = 60 * 1000;\n\n/** A Trello board, surfaced as both a \"resource\" (org analogue) and a \"project\" for issue reads. */\nexport interface TrelloBoardDescriptor extends IssueResourceDescriptor {}\n\nexport class TrelloIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Trello> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\n\toverride get id(): IssuesCloudHostIntegrationId.Trello {\n\t\treturn IssuesCloudHostIntegrationId.Trello;\n\t}\n\tprotected override get key(): 'trello' {\n\t\treturn 'trello';\n\t}\n\toverride get name(): string {\n\t\treturn metadata.name;\n\t}\n\toverride get domain(): string {\n\t\treturn metadata.domain;\n\t}\n\n\t/** The Trello client requires the app key paired with the token; a session without one can't read. */\n\tprivate appKeyFor(session: ProviderAuthenticationSession): string | undefined {\n\t\treturn session.appKey;\n\t}\n\n\t/**\n\t * Resolves the app key or throws — a session that authenticated but carries no app key can't read Trello,\n\t * and must be surfaced as a broken read (warning + fetchFailed) rather than an empty account.\n\t */\n\tprivate requireAppKey(session: ProviderAuthenticationSession): string {\n\t\tconst appKey = this.appKeyFor(session);\n\t\tif (appKey == null) {\n\t\t\tthrow new IntegrationReadUnavailableError(metadata.name, 'missing app key (session has no appKey)');\n\t\t}\n\t\treturn appKey;\n\t}\n\n\t/**\n\t * Always asks Trello: the host's current-account cache decides when this runs. The member lookup is\n\t * account-wide, so the answer is lent briefly to the per-board lookup a tracker read makes right after it.\n\t */\n\tprotected override async getProviderCurrentAccount(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<Account | undefined> {\n\t\tconst account = await this.fetchAccount(session);\n\t\tif (account != null) {\n\t\t\tthis._recentAccounts.set(session.accessToken, Promise.resolve(account));\n\t\t}\n\t\treturn account;\n\t}\n\n\tprotected override getProviderUncachedAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\treturn this.fetchAccount(session, this.requireAppKey(session));\n\t}\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\tconst appKey = this.requireAppKey(session);\n\t\treturn this._recentAccounts.getOrCreate(session.accessToken, () => this.fetchAccount(session, appKey), {\n\t\t\tevictWhen: account => account == null,\n\t\t});\n\t}\n\n\toverride invalidateDiscoveryCaches(): void {\n\t\tthis._recentAccounts.clear();\n\t}\n\n\tprivate readonly _recentAccounts = new PromiseCache<string, Account | undefined>({\n\t\tcapacity: 10,\n\t\tcreateTTL: recentAccountTTL,\n\t});\n\n\tprivate async fetchAccount(\n\t\tsession: ProviderAuthenticationSession,\n\t\tappKey: string = this.requireAppKey(session),\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await api.getTrelloCurrentUser(toTokenWithInfo(this.id, session), appKey);\n\t\tif (user == null) return undefined;\n\n\t\treturn {\n\t\t\tprovider: this,\n\t\t\tid: user.id,\n\t\t\tname: user.name,\n\t\t\tusername: user.username,\n\t\t\temail: user.email,\n\t\t\tavatarUrl: user.avatarUrl ?? undefined,\n\t\t};\n\t}\n\n\tprotected override async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<TrelloBoardDescriptor[] | undefined> {\n\t\tconst appKey = this.requireAppKey(session);\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst boards = await api.getTrelloBoardsForCurrentUser(toTokenWithInfo(this.id, session), appKey);\n\t\treturn boards?.map(b => ({ key: b.id, id: b.id, name: b.name }));\n\t}\n\n\tprotected override getProviderProjectsForResources(\n\t\t_session: ProviderAuthenticationSession,\n\t\tresources: ResourceDescriptor[],\n\t): Promise<TrelloBoardDescriptor[] | undefined> {\n\t\t// Trello boards are both the resource and the project scope, so projects are the boards themselves.\n\t\treturn Promise.resolve(\n\t\t\tresources.filter(isIssueResourceDescriptor).map(r => ({ key: r.key, id: r.id, name: r.name })),\n\t\t);\n\t}\n\n\tprotected override async getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: ResourceDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;\n\t}\n\n\tprotected override async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: ResourceDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\t// A non-issue descriptor genuinely has nothing to read (empty), but a missing app key is a broken read.\n\t\tif (!isIssueResourceDescriptor(project)) return undefined;\n\n\t\t// The board read always excludes archived cards, and a card has no other state, so only open cards can be\n\t\t// read. Refused rather than ignored: answering `closed` with the open cards would publish them as closed.\n\t\tif (options?.state != null && options.state !== 'open') {\n\t\t\tthrow new IntegrationReadUnavailableError(metadata.name, `issue state '${options.state}' is not supported`);\n\t\t}\n\n\t\tconst appKey = this.requireAppKey(session);\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\n\t\t// Enrich issues with their board list names (card status), mirroring gitkraken.dev's Trello reads.\n\t\tconst lists = await api.getTrelloListsForBoard(tokenWithInfo, appKey, project.id);\n\t\tconst trelloBoardListsById = lists?.reduce<Record<string, { name: string }>>((acc, list) => {\n\t\t\tacc[list.id] = { name: list.name };\n\t\t\treturn acc;\n\t\t}, {});\n\t\tconst boardProject = {\n\t\t\tid: project.id,\n\t\t\tname: project.name,\n\t\t\tresourceId: project.id,\n\t\t\tresourceName: project.name,\n\t\t};\n\n\t\tconst result = await api.getTrelloIssuesForBoard(tokenWithInfo, appKey, project.id, {\n\t\t\tassigneeLogins: options?.user != null ? [options.user] : undefined,\n\t\t\ttrelloBoardListsById: trelloBoardListsById,\n\t\t\tsort: options?.sort,\n\t\t});\n\n\t\tconst values = result.values.flatMap(issue => {\n\t\t\tconst mapped = toIssueShape(issue, this, { projection: 'project' });\n\t\t\treturn mapped == null ? [] : [{ ...mapped, project: boardProject } satisfies IssueShape];\n\t\t});\n\n\t\t// Trello's search caps results and reports the cap through `metadata.completeness` (partial/unknown),\n\t\t// never a cursor. Surface that as terminal truncation; there is no next page to fetch, so retrying the\n\t\t// same read cannot recover the omitted cards (D11).\n\t\tconst truncated = result.metadata != null && result.metadata.completeness !== 'complete';\n\t\treturn truncated\n\t\t\t? { values: values, truncated: true, recovery: 'none', metadata: result.metadata }\n\t\t\t: { values: values, truncated: false, metadata: result.metadata };\n\t}\n\n\tprotected override searchProviderMyIssues(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_resources?: ResourceDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\t// Trello has no cross-board \"issues assigned to me\" endpoint; issues are read per board via\n\t\t// getIssuesForProject. Callers that need them enumerate boards first.\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\tprotected override getProviderLinkedIssueOrPullRequest(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t\t_id: { id: string; key: string },\n\t\t_type: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tif (!isIssueResourceDescriptor(resource)) return undefined;\n\n\t\tconst appKey = this.requireAppKey(session);\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst lists = await api.getTrelloListsForBoard(tokenWithInfo, appKey, resource.id);\n\t\tconst trelloBoardListsById = lists?.reduce<Record<string, { name: string }>>((acc, list) => {\n\t\t\tacc[list.id] = { name: list.name };\n\t\t\treturn acc;\n\t\t}, {});\n\n\t\t// Branch-association round-trips use the encoded entity identifier's stable Trello card id. Resolve that\n\t\t// directly so large boards don't require a capped whole-board scan; keep a numeric idShort fallback so\n\t\t// legacy/manual callers that still pass the board-local display number continue to resolve.\n\t\tlet issue = await api.getTrelloCard(tokenWithInfo, appKey, id, {\n\t\t\ttrelloBoardListsById: trelloBoardListsById,\n\t\t});\n\t\tif (issue == null && /^[0-9]+$/.test(id)) {\n\t\t\tissue = (\n\t\t\t\tawait api.getTrelloIssuesForBoard(tokenWithInfo, appKey, resource.id, {\n\t\t\t\t\ttrelloBoardListsById: trelloBoardListsById,\n\t\t\t\t})\n\t\t\t).values.find(issue => issue.number === id);\n\t\t}\n\n\t\treturn issue != null\n\t\t\t? fromProviderIssue(issue, this, {\n\t\t\t\t\tprojection: 'point',\n\t\t\t\t\tproject: {\n\t\t\t\t\t\tid: resource.id,\n\t\t\t\t\t\tname: resource.name,\n\t\t\t\t\t\tresourceId: resource.id,\n\t\t\t\t\t\tresourceName: resource.name,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t: undefined;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"trello.js","names":[],"sources":["../../../../../plus/integrations/src/providers/trello.ts"],"sourcesContent":["import type { Account } from '@gitlens/git/models/author.js';\nimport type { Issue, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestType } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { IssueResourceDescriptor, ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport { isIssueResourceDescriptor } from '@gitlens/git/utils/resourceDescriptor.utils.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { IssuesCloudHostIntegrationId } from '../constants.js';\nimport { IntegrationReadUnavailableError } from '../errors.js';\nimport type { IssuesForProjectOptions, ProjectIssuesDrain } from '../models/issueReads.js';\nimport { IssuesIntegration } from '../models/issuesIntegration.js';\nimport { fromProviderIssue, providersMetadata, toIssueShape } from './models.js';\n\nconst metadata = providersMetadata[IssuesCloudHostIntegrationId.Trello];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\n/** Long enough to span one tracker read, short enough that a profile change shows on the next. */\nconst recentAccountTTL = 60 * 1000;\n\n/** A Trello board, surfaced as both a \"resource\" (org analogue) and a \"project\" for issue reads. */\nexport interface TrelloBoardDescriptor extends IssueResourceDescriptor {}\n\nexport class TrelloIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Trello> {\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\n\toverride get id(): IssuesCloudHostIntegrationId.Trello {\n\t\treturn IssuesCloudHostIntegrationId.Trello;\n\t}\n\tprotected override get key(): 'trello' {\n\t\treturn 'trello';\n\t}\n\toverride get name(): string {\n\t\treturn metadata.name;\n\t}\n\toverride get domain(): string {\n\t\treturn metadata.domain;\n\t}\n\n\t/** The Trello client requires the app key paired with the token; a session without one can't read. */\n\tprivate appKeyFor(session: ProviderAuthenticationSession): string | undefined {\n\t\treturn session.appKey;\n\t}\n\n\t/**\n\t * Resolves the app key or throws — a session that authenticated but carries no app key can't read Trello,\n\t * and must be surfaced as a broken read (warning + fetchFailed) rather than an empty account.\n\t */\n\tprivate requireAppKey(session: ProviderAuthenticationSession): string {\n\t\tconst appKey = this.appKeyFor(session);\n\t\tif (appKey == null) {\n\t\t\tthrow new IntegrationReadUnavailableError(metadata.name, 'missing app key (session has no appKey)');\n\t\t}\n\t\treturn appKey;\n\t}\n\n\t/**\n\t * Always asks Trello: the host's current-account cache decides when this runs. The member lookup is\n\t * account-wide, so the answer is lent briefly to the per-board lookup a tracker read makes right after it.\n\t */\n\tprotected override async getProviderCurrentAccount(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<Account | undefined> {\n\t\tconst account = await this.fetchAccount(session);\n\t\tif (account != null) {\n\t\t\tthis._recentAccounts.set(session.accessToken, Promise.resolve(account));\n\t\t}\n\t\treturn account;\n\t}\n\n\tprotected override getProviderUncachedAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\treturn this.fetchAccount(session, this.requireAppKey(session));\n\t}\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\tconst appKey = this.requireAppKey(session);\n\t\treturn this._recentAccounts.getOrCreate(session.accessToken, () => this.fetchAccount(session, appKey), {\n\t\t\tevictWhen: account => account == null,\n\t\t});\n\t}\n\n\tprotected override clearDiscoveryCaches(): void {\n\t\tthis._recentAccounts.clear();\n\t}\n\n\tprivate readonly _recentAccounts = new PromiseCache<string, Account | undefined>({\n\t\tcapacity: 10,\n\t\tcreateTTL: recentAccountTTL,\n\t});\n\n\tprivate async fetchAccount(\n\t\tsession: ProviderAuthenticationSession,\n\t\tappKey: string = this.requireAppKey(session),\n\t): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst user = await api.getTrelloCurrentUser(toTokenWithInfo(this.id, session), appKey);\n\t\tif (user == null) return undefined;\n\n\t\treturn {\n\t\t\tprovider: this,\n\t\t\tid: user.id,\n\t\t\tname: user.name,\n\t\t\tusername: user.username,\n\t\t\temail: user.email,\n\t\t\tavatarUrl: user.avatarUrl ?? undefined,\n\t\t};\n\t}\n\n\tprotected override async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<TrelloBoardDescriptor[] | undefined> {\n\t\tconst appKey = this.requireAppKey(session);\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst boards = await api.getTrelloBoardsForCurrentUser(toTokenWithInfo(this.id, session), appKey);\n\t\treturn boards?.map(b => ({ key: b.id, id: b.id, name: b.name }));\n\t}\n\n\tprotected override getProviderProjectsForResources(\n\t\t_session: ProviderAuthenticationSession,\n\t\tresources: ResourceDescriptor[],\n\t): Promise<TrelloBoardDescriptor[] | undefined> {\n\t\t// Trello boards are both the resource and the project scope, so projects are the boards themselves.\n\t\treturn Promise.resolve(\n\t\t\tresources.filter(isIssueResourceDescriptor).map(r => ({ key: r.key, id: r.id, name: r.name })),\n\t\t);\n\t}\n\n\tprotected override async getProviderIssuesForProject(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: ResourceDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.getProviderIssuesForProjectWithTruncation(session, project, options))?.values;\n\t}\n\n\tprotected override async getProviderIssuesForProjectWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tproject: ResourceDescriptor,\n\t\toptions?: IssuesForProjectOptions,\n\t): Promise<ProjectIssuesDrain | undefined> {\n\t\t// A non-issue descriptor genuinely has nothing to read (empty), but a missing app key is a broken read.\n\t\tif (!isIssueResourceDescriptor(project)) return undefined;\n\n\t\t// The board read always excludes archived cards, and a card has no other state, so only open cards can be\n\t\t// read. Refused rather than ignored: answering `closed` with the open cards would publish them as closed.\n\t\tif (options?.state != null && options.state !== 'open') {\n\t\t\tthrow new IntegrationReadUnavailableError(metadata.name, `issue state '${options.state}' is not supported`);\n\t\t}\n\n\t\tconst appKey = this.requireAppKey(session);\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\n\t\t// Enrich issues with their board list names (card status), mirroring gitkraken.dev's Trello reads.\n\t\tconst lists = await api.getTrelloListsForBoard(tokenWithInfo, appKey, project.id);\n\t\tconst trelloBoardListsById = lists?.reduce<Record<string, { name: string }>>((acc, list) => {\n\t\t\tacc[list.id] = { name: list.name };\n\t\t\treturn acc;\n\t\t}, {});\n\t\tconst boardProject = {\n\t\t\tid: project.id,\n\t\t\tname: project.name,\n\t\t\tresourceId: project.id,\n\t\t\tresourceName: project.name,\n\t\t};\n\n\t\tconst result = await api.getTrelloIssuesForBoard(tokenWithInfo, appKey, project.id, {\n\t\t\tassigneeLogins: options?.user != null ? [options.user] : undefined,\n\t\t\ttrelloBoardListsById: trelloBoardListsById,\n\t\t\tsort: options?.sort,\n\t\t});\n\n\t\tconst values = result.values.flatMap(issue => {\n\t\t\tconst mapped = toIssueShape(issue, this, { projection: 'project' });\n\t\t\treturn mapped == null ? [] : [{ ...mapped, project: boardProject } satisfies IssueShape];\n\t\t});\n\n\t\t// Trello's search caps results and reports the cap through `metadata.completeness` (partial/unknown),\n\t\t// never a cursor. Surface that as terminal truncation; there is no next page to fetch, so retrying the\n\t\t// same read cannot recover the omitted cards (D11).\n\t\tconst truncated = result.metadata != null && result.metadata.completeness !== 'complete';\n\t\treturn truncated\n\t\t\t? { values: values, truncated: true, recovery: 'none', metadata: result.metadata }\n\t\t\t: { values: values, truncated: false, metadata: result.metadata };\n\t}\n\n\tprotected override searchProviderMyIssues(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_resources?: ResourceDescriptor[],\n\t\t_cancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\t// Trello has no cross-board \"issues assigned to me\" endpoint; issues are read per board via\n\t\t// getIssuesForProject. Callers that need them enumerate boards first.\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\tprotected override getProviderLinkedIssueOrPullRequest(\n\t\t_session: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t\t_id: { id: string; key: string },\n\t\t_type: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tif (!isIssueResourceDescriptor(resource)) return undefined;\n\n\t\tconst appKey = this.requireAppKey(session);\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tconst lists = await api.getTrelloListsForBoard(tokenWithInfo, appKey, resource.id);\n\t\tconst trelloBoardListsById = lists?.reduce<Record<string, { name: string }>>((acc, list) => {\n\t\t\tacc[list.id] = { name: list.name };\n\t\t\treturn acc;\n\t\t}, {});\n\n\t\t// Branch-association round-trips use the encoded entity identifier's stable Trello card id. Resolve that\n\t\t// directly so large boards don't require a capped whole-board scan; keep a numeric idShort fallback so\n\t\t// legacy/manual callers that still pass the board-local display number continue to resolve.\n\t\tlet issue = await api.getTrelloCard(tokenWithInfo, appKey, id, {\n\t\t\ttrelloBoardListsById: trelloBoardListsById,\n\t\t});\n\t\tif (issue == null && /^[0-9]+$/.test(id)) {\n\t\t\tissue = (\n\t\t\t\tawait api.getTrelloIssuesForBoard(tokenWithInfo, appKey, resource.id, {\n\t\t\t\t\ttrelloBoardListsById: trelloBoardListsById,\n\t\t\t\t})\n\t\t\t).values.find(issue => issue.number === id);\n\t\t}\n\n\t\treturn issue != null\n\t\t\t? fromProviderIssue(issue, this, {\n\t\t\t\t\tprojection: 'point',\n\t\t\t\t\tproject: {\n\t\t\t\t\t\tid: resource.id,\n\t\t\t\t\t\tname: resource.name,\n\t\t\t\t\t\tresourceId: resource.id,\n\t\t\t\t\t\tresourceName: resource.name,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t: undefined;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ResourceDescriptor } from "../../../../git/models/resourceDescriptor.js";
|
|
2
|
+
import { AutolinkReference } from "../../../../git/models/autolink.js";
|
|
3
|
+
//#region ../plus/integrations/src/providers/utils/issueAutolinks.d.ts
|
|
4
|
+
export declare function createIssueAutolinkReferences(key: string, url: string, resource: ResourceDescriptor & {
|
|
5
|
+
name: string;
|
|
6
|
+
}): AutolinkReference[];
|
|
7
|
+
//#endregion
|
|
8
|
+
//# sourceMappingURL=issueAutolinks.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as l10n from "@vscode/l10n";
|
|
2
|
+
//#region ../plus/integrations/src/providers/utils/issueAutolinks.ts
|
|
3
|
+
function createIssueAutolinkReferences(key, url, resource) {
|
|
4
|
+
const dashedPrefix = `${key}-`;
|
|
5
|
+
return [dashedPrefix, `${key}_`].map((prefix) => ({
|
|
6
|
+
prefix,
|
|
7
|
+
url,
|
|
8
|
+
alphanumeric: false,
|
|
9
|
+
ignoreCase: false,
|
|
10
|
+
referenceType: prefix === dashedPrefix ? void 0 : "branch",
|
|
11
|
+
title: l10n.t("Open Issue {0} on {1}", `${dashedPrefix}<num>`, resource.name),
|
|
12
|
+
type: "issue",
|
|
13
|
+
description: l10n.t("{0} Issue {1}", resource.name, `${dashedPrefix}<num>`),
|
|
14
|
+
descriptor: { ...resource }
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { createIssueAutolinkReferences };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=issueAutolinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issueAutolinks.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/utils/issueAutolinks.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport type { AutolinkReference } from '@gitlens/git/models/autolink.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\n\nexport function createIssueAutolinkReferences(\n\tkey: string,\n\turl: string,\n\tresource: ResourceDescriptor & { name: string },\n): AutolinkReference[] {\n\tconst dashedPrefix = `${key}-`;\n\treturn [dashedPrefix, `${key}_`].map<AutolinkReference>(prefix => ({\n\t\tprefix: prefix,\n\t\turl: url,\n\t\talphanumeric: false,\n\t\tignoreCase: false,\n\t\treferenceType: prefix === dashedPrefix ? undefined : 'branch',\n\t\ttitle: l10n.t('Open Issue {0} on {1}', `${dashedPrefix}<num>`, resource.name),\n\t\ttype: 'issue',\n\t\tdescription: l10n.t('{0} Issue {1}', resource.name, `${dashedPrefix}<num>`),\n\t\tdescriptor: { ...resource },\n\t}));\n}\n"],"mappings":";;AAIA,SAAgB,8BACf,KACA,KACA,UACsB;CACtB,MAAM,eAAe,GAAG,IAAI;CAC5B,OAAO,CAAC,cAAc,GAAG,IAAI,EAAE,CAAC,CAAC,KAAuB,YAAW;EAC1D;EACH;EACL,cAAc;EACd,YAAY;EACZ,eAAe,WAAW,eAAe,KAAA,IAAY;EACrD,OAAO,KAAK,EAAE,yBAAyB,GAAG,aAAa,QAAQ,SAAS,IAAI;EAC5E,MAAM;EACN,aAAa,KAAK,EAAE,iBAAiB,SAAS,MAAM,GAAG,aAAa,MAAM;EAC1E,YAAY,EAAE,GAAG,SAAS;CAC3B,EAAE;AACH"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RefSet } from "../../../git/models/autolink.js";
|
|
2
|
+
import { ProviderReadContext } from "./context.js";
|
|
3
|
+
import { GetAutolinkReferencesOptions } from "../manager.js";
|
|
4
|
+
//#region ../plus/integrations/src/reads/autolinks.d.ts
|
|
5
|
+
export declare function getAutolinkReferences(ctx: ProviderReadContext, options: GetAutolinkReferencesOptions): Promise<RefSet | undefined>;
|
|
6
|
+
//#endregion
|
|
7
|
+
//# sourceMappingURL=autolinks.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import "../constants.js";
|
|
2
|
+
import { isIncompleteCollection } from "../collectionMetadata.js";
|
|
3
|
+
import { isIssuesIntegration } from "../models/issuesIntegration.js";
|
|
4
|
+
import { createIssueAutolinkReferences } from "../providers/utils/issueAutolinks.js";
|
|
5
|
+
//#region ../plus/integrations/src/reads/autolinks.ts
|
|
6
|
+
async function getAutolinkReferences(ctx, options) {
|
|
7
|
+
if (options.providerId !== "jira" && options.providerId !== "jira-server") return;
|
|
8
|
+
const integration = await ctx.getIntegrationForRead(options.providerId, options.connectionId, options.domain);
|
|
9
|
+
if (integration == null || !isIssuesIntegration(integration)) return void 0;
|
|
10
|
+
try {
|
|
11
|
+
await ctx.forceRefreshIfRequested(integration, options.forceSync, options.connectionId, { throwOnFailure: true });
|
|
12
|
+
} catch {
|
|
13
|
+
return;
|
|
14
|
+
} finally {
|
|
15
|
+
if (options.forceSync) integration.invalidateDiscoveryForConnection(options.connectionId);
|
|
16
|
+
}
|
|
17
|
+
const result = await integration.getUncachedDiscoveryResult(() => true, options.connectionId);
|
|
18
|
+
const discovery = result?.value;
|
|
19
|
+
if (result?.error != null || discovery?.resources == null || discovery.projects == null || discovery.generation !== integration.getSessionDiscoveryGeneration(discovery.sessionId, options.connectionId, discovery.sessionCloud) || isIncompleteCollection(discovery.projects.metadata)) return;
|
|
20
|
+
const refs = [];
|
|
21
|
+
for (const project of discovery.projects.values) {
|
|
22
|
+
if (options.providerId === "jira-server" && project.key === project.id) continue;
|
|
23
|
+
const resource = discovery.resources.find((r) => r.id === project.resourceId);
|
|
24
|
+
if (resource == null || typeof resource.name !== "string" || typeof resource.url !== "string") return void 0;
|
|
25
|
+
refs.push(...createIssueAutolinkReferences(project.key, `${resource.url}/browse/${project.key}-<num>`, {
|
|
26
|
+
...resource,
|
|
27
|
+
name: resource.name
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
return [{
|
|
31
|
+
id: integration.id,
|
|
32
|
+
name: integration.name,
|
|
33
|
+
domain: integration.domain,
|
|
34
|
+
icon: integration.icon
|
|
35
|
+
}, refs];
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { getAutolinkReferences };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=autolinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autolinks.js","names":[],"sources":["../../../../../plus/integrations/src/reads/autolinks.ts"],"sourcesContent":["import type { RefSet } from '@gitlens/git/models/autolink.js';\nimport { isIncompleteCollection } from '../collectionMetadata.js';\nimport { IssuesCloudHostIntegrationId, IssuesSelfManagedHostIntegrationId } from '../constants.js';\nimport type { GetAutolinkReferencesOptions } from '../manager.js';\nimport { isIssuesIntegration } from '../models/issuesIntegration.js';\nimport { createIssueAutolinkReferences } from '../providers/utils/issueAutolinks.js';\nimport type { ProviderReadContext } from './context.js';\n\nexport async function getAutolinkReferences(\n\tctx: ProviderReadContext,\n\toptions: GetAutolinkReferencesOptions,\n): Promise<RefSet | undefined> {\n\tif (\n\t\toptions.providerId !== IssuesCloudHostIntegrationId.Jira &&\n\t\toptions.providerId !== IssuesSelfManagedHostIntegrationId.JiraServer\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst integration = await ctx.getIntegrationForRead(options.providerId, options.connectionId, options.domain);\n\tif (integration == null || !isIssuesIntegration(integration)) return undefined;\n\n\ttry {\n\t\tawait ctx.forceRefreshIfRequested(integration, options.forceSync, options.connectionId, {\n\t\t\tthrowOnFailure: true,\n\t\t});\n\t} catch {\n\t\treturn undefined;\n\t} finally {\n\t\tif (options.forceSync) {\n\t\t\t// Completion retires reads started during the refresh, including when it failed.\n\t\t\tintegration.invalidateDiscoveryForConnection(options.connectionId);\n\t\t}\n\t}\n\n\tconst result = await integration.getUncachedDiscoveryResult(() => true, options.connectionId);\n\tconst discovery = result?.value;\n\tif (\n\t\tresult?.error != null ||\n\t\tdiscovery?.resources == null ||\n\t\tdiscovery.projects == null ||\n\t\tdiscovery.generation !==\n\t\t\tintegration.getSessionDiscoveryGeneration(\n\t\t\t\tdiscovery.sessionId,\n\t\t\t\toptions.connectionId,\n\t\t\t\tdiscovery.sessionCloud,\n\t\t\t) ||\n\t\tisIncompleteCollection(discovery.projects.metadata)\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst refs: RefSet[1] = [];\n\tfor (const project of discovery.projects.values) {\n\t\tif (options.providerId === IssuesSelfManagedHostIntegrationId.JiraServer && project.key === project.id) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst resource = discovery.resources.find(r => r.id === project.resourceId);\n\t\tif (resource == null || typeof resource.name !== 'string' || typeof resource.url !== 'string') return undefined;\n\n\t\trefs.push(\n\t\t\t...createIssueAutolinkReferences(project.key, `${resource.url}/browse/${project.key}-<num>`, {\n\t\t\t\t...resource,\n\t\t\t\tname: resource.name,\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn [{ id: integration.id, name: integration.name, domain: integration.domain, icon: integration.icon }, refs];\n}\n"],"mappings":";;;;;AAQA,eAAsB,sBACrB,KACA,SAC8B;CAC9B,IACC,QAAQ,eAAA,UACR,QAAQ,eAAA,eAER;CAGD,MAAM,cAAc,MAAM,IAAI,sBAAsB,QAAQ,YAAY,QAAQ,cAAc,QAAQ,MAAM;CAC5G,IAAI,eAAe,QAAQ,CAAC,oBAAoB,WAAW,GAAG,OAAO,KAAA;CAErE,IAAI;EACH,MAAM,IAAI,wBAAwB,aAAa,QAAQ,WAAW,QAAQ,cAAc,EACvF,gBAAgB,KACjB,CAAC;CACF,QAAQ;EACP;CACD,UAAU;EACT,IAAI,QAAQ,WAEX,YAAY,iCAAiC,QAAQ,YAAY;CAEnE;CAEA,MAAM,SAAS,MAAM,YAAY,iCAAiC,MAAM,QAAQ,YAAY;CAC5F,MAAM,YAAY,QAAQ;CAC1B,IACC,QAAQ,SAAS,QACjB,WAAW,aAAa,QACxB,UAAU,YAAY,QACtB,UAAU,eACT,YAAY,8BACX,UAAU,WACV,QAAQ,cACR,UAAU,YACX,KACD,uBAAuB,UAAU,SAAS,QAAQ,GAElD;CAGD,MAAM,OAAkB,CAAC;CACzB,KAAK,MAAM,WAAW,UAAU,SAAS,QAAQ;EAChD,IAAI,QAAQ,eAAA,iBAAgE,QAAQ,QAAQ,QAAQ,IACnG;EAGD,MAAM,WAAW,UAAU,UAAU,MAAK,MAAK,EAAE,OAAO,QAAQ,UAAU;EAC1E,IAAI,YAAY,QAAQ,OAAO,SAAS,SAAS,YAAY,OAAO,SAAS,QAAQ,UAAU,OAAO,KAAA;EAEtG,KAAK,KACJ,GAAG,8BAA8B,QAAQ,KAAK,GAAG,SAAS,IAAI,UAAU,QAAQ,IAAI,SAAS;GAC5F,GAAG;GACH,MAAM,SAAS;EAChB,CAAC,CACF;CACD;CAEA,OAAO,CAAC;EAAE,IAAI,YAAY;EAAI,MAAM,YAAY;EAAM,QAAQ,YAAY;EAAQ,MAAM,YAAY;CAAK,GAAG,IAAI;AACjH"}
|
|
@@ -43,8 +43,10 @@ export interface ProviderReadContext {
|
|
|
43
43
|
warnings: ProviderWarning[];
|
|
44
44
|
fetchFailed: boolean;
|
|
45
45
|
};
|
|
46
|
-
/** Forces a session refresh before a read when the caller asked for one. Best-effort
|
|
47
|
-
forceRefreshIfRequested(integration: Integration, forceSync: boolean | undefined, connectionId: string | undefined
|
|
46
|
+
/** Forces a session refresh before a read when the caller asked for one. Best-effort unless strict failure propagation is requested. */
|
|
47
|
+
forceRefreshIfRequested(integration: Integration, forceSync: boolean | undefined, connectionId: string | undefined, options?: {
|
|
48
|
+
throwOnFailure?: boolean;
|
|
49
|
+
}): Promise<void>;
|
|
48
50
|
/** The configured connection descriptors, as `getConfigured` exposes them. */
|
|
49
51
|
getConfigured(id?: IntegrationIds, options?: {
|
|
50
52
|
cloud?: boolean;
|
|
@@ -2,7 +2,6 @@ import { ResourceDescriptor } from "../../../git/models/resourceDescriptor.js";
|
|
|
2
2
|
import { PullRequest, PullRequestFilter, PullRequestStateFilter } from "../../../git/models/pullRequest.js";
|
|
3
3
|
import { IntegrationIds } from "../constants.js";
|
|
4
4
|
import { ProviderWarning } from "../results.js";
|
|
5
|
-
import "../providerFilters.js";
|
|
6
5
|
import { ProviderPullRequest, ProviderReposInput, ProviderRepository } from "../providers/models.js";
|
|
7
6
|
import { GitHostIntegration } from "../models/gitHostIntegration.js";
|
|
8
7
|
import { IssuesIntegration } from "../models/issuesIntegration.js";
|
|
@@ -4,10 +4,10 @@ import { appendDedupedWarning } from "../results.js";
|
|
|
4
4
|
import { mergeAssessmentInto } from "../collectionMetadata.js";
|
|
5
5
|
import { isGitHostIntegration, isIssuesHostIntegrationId, warnOnMissingSessionForDomain } from "../utils/integration.utils.js";
|
|
6
6
|
import { toProviderRepositoryShape } from "../providers/models.js";
|
|
7
|
+
import { isIssuesIntegration } from "../models/issuesIntegration.js";
|
|
7
8
|
import { pageToCursor, refusedPage, resolveContinuation, resolveCurrentPage, toProviderPageInfo } from "./paging.js";
|
|
8
9
|
import { gitHostOnlySurfaceWarning, truncationWarning, unsupportedWarning } from "./warnings.js";
|
|
9
10
|
import { discoverResources, runCaptured } from "./drains.js";
|
|
10
|
-
import { isIssuesIntegration } from "../models/issuesIntegration.js";
|
|
11
11
|
import { assertHierarchyReadTarget, mergeHierarchyResults, projectToOrg, resourceMatchesOrg, resourceToOrg, withProviderContext } from "./hierarchy.utils.js";
|
|
12
12
|
//#region ../plus/integrations/src/reads/hierarchy.ts
|
|
13
13
|
/**
|
|
@@ -3,9 +3,9 @@ import { githubGraphQLInt32Max, isAzureProviderId, isGitHubProviderId } from "..
|
|
|
3
3
|
import { areDomainsOnSameHost, hostFromDomain } from "../utils/domain.utils.js";
|
|
4
4
|
import { isGitHostIntegration, isIssuesHostIntegrationId, isIssuesSelfManagedHostIntegrationId } from "../utils/integration.utils.js";
|
|
5
5
|
import { issueEtagIncludes } from "../models/integration.js";
|
|
6
|
+
import { isIssuesIntegration } from "../models/issuesIntegration.js";
|
|
6
7
|
import { gitHostOnlySurfaceWarning, issueTrackerOnlySurfaceWarning, issuesUnsupportedWarning, noConnectionWarning, otherWarning, unsupportedWarning } from "./warnings.js";
|
|
7
8
|
import { runCaptured } from "./drains.js";
|
|
8
|
-
import { isIssuesIntegration } from "../models/issuesIntegration.js";
|
|
9
9
|
import { readEtaggedBatch } from "./batchEtags.js";
|
|
10
10
|
import { findInvalidIssueEtagInclude, issueEtag, issueEtagFieldsFromShape, normalizeIssueEtagIncludes } from "./etag.js";
|
|
11
11
|
//#region ../plus/integrations/src/reads/issueBatch.ts
|
|
@@ -7,10 +7,10 @@ import "../providerFilters.js";
|
|
|
7
7
|
import { isIssuesHostIntegrationId } from "../utils/integration.utils.js";
|
|
8
8
|
import { mergeCollectionMetadata, parsePageCursor } from "../providers/utils/providerPaging.js";
|
|
9
9
|
import { providersMetadata } from "../providers/models.js";
|
|
10
|
+
import { isIssuesIntegration } from "../models/issuesIntegration.js";
|
|
10
11
|
import { parseIssueTrackerPageCursor, toIssueTrackerPageCursor } from "./cursors.js";
|
|
11
12
|
import { incompleteReadWarning, issueTrackerOnlySurfaceWarning, otherWarning, projectCutWarning, unmatchedSelectorWarning, unmergeableIssueSortWarning, unsupportedIssueSortWarning, unsupportedWarning } from "./warnings.js";
|
|
12
13
|
import { discoverResources, runCaptured } from "./drains.js";
|
|
13
|
-
import { isIssuesIntegration } from "../models/issuesIntegration.js";
|
|
14
14
|
import { projectKey, resourceIdForProject, resourceLabel, selectorMatches } from "./hierarchy.utils.js";
|
|
15
15
|
import { resolveIssueSort, toIssueOrdering } from "./ordering.js";
|
|
16
16
|
import { confirmCredential, confirmSelection, toSelectionAction } from "./selectionConfirmation.js";
|
|
@@ -7,8 +7,8 @@ import { mergeAssessmentInto } from "../collectionMetadata.js";
|
|
|
7
7
|
import { decodePathSegment, getRemoteHostMatcher, hostFromDomain, isWebRemoteScheme } from "../utils/domain.utils.js";
|
|
8
8
|
import { getIntegrationIdForRemote, getSelfManagedBaseUrl, isGitCloudHostIntegrationId, isGitSelfManagedHostIntegrationId, remoteProviderTypeForConfig, remoteProviderTypeForIntegration } from "../utils/integration.utils.js";
|
|
9
9
|
import { isAzureCloudDomain, isBitbucketCloudDomain, isGitHubDotCom, isGitLabDotCom } from "../providers/models.js";
|
|
10
|
-
import { noConnectionWarning } from "./warnings.js";
|
|
11
10
|
import { isIssuesIntegration } from "../models/issuesIntegration.js";
|
|
11
|
+
import { noConnectionWarning } from "./warnings.js";
|
|
12
12
|
import { getAzureRepositoryIdentity } from "../providers/azure/azure.utils.js";
|
|
13
13
|
//#region ../plus/integrations/src/reads/resolveRepository.ts
|
|
14
14
|
/**
|
package/docs/integrations.md
CHANGED
|
@@ -1220,7 +1220,28 @@ every read below is scoped to that one connection; a paged read that omits it ge
|
|
|
1220
1220
|
while `getIssuesBatch` refuses instead (see §4). `listOrgs` returns exactly one resource — the
|
|
1221
1221
|
instance itself, synthesized from the configured host rather than fetched — and its projects carry the project key
|
|
1222
1222
|
as `key` (the numeric id when the server reports none), which is what `listProjects` publishes as `name` and what
|
|
1223
|
-
`project` selects; reads address the project by id regardless.
|
|
1223
|
+
`project` selects; reads address the project by id regardless. Project autolinks use the real key, skipping
|
|
1224
|
+
projects whose key fell back to the numeric id. Dashed commit references (`PROJ-12`) and underscored branch
|
|
1225
|
+
references (`PROJ_12`) both point to `<configured instance URL>/browse/PROJ-12`, retaining any context path.
|
|
1226
|
+
|
|
1227
|
+
`getAutolinkReferences({ providerId, connectionId?, domain?, forceSync? })` supports Jira Cloud and Data Center.
|
|
1228
|
+
It returns a `RefSet` tuple containing provider metadata and references for that one connection, or `undefined`
|
|
1229
|
+
when the target cannot be resolved or discovery failed or was incomplete. Each call awaits uncached discovery
|
|
1230
|
+
with one resolved session; callers own coalescing and caching. `forceSync: true` refreshes the session and
|
|
1231
|
+
retires discovery started before or during that refresh. Refreshing a secondary connection does not change the
|
|
1232
|
+
primary session. Empty references mean complete discovery found no projects with links. Internal decoration
|
|
1233
|
+
readers retain their separate tolerant cache. Match references in the host with `getAutolinks` or `getBranchAutolinks` from
|
|
1234
|
+
`git/utils/autolink.utils.js`; references may hold compiled regexes and are not a wire format. Match separate
|
|
1235
|
+
references independently if distinct projects sharing an issue number must both be retained, since
|
|
1236
|
+
`getAutolinks` keys its result by the numeric suffix.
|
|
1237
|
+
|
|
1238
|
+
`getAutolinks(message, refsets, { maxResults })` stops static-reference materialization when the result map
|
|
1239
|
+
reaches the requested number of distinct entries, including entries inserted by dynamic callbacks. Omit the
|
|
1240
|
+
option for existing unlimited behavior; zero returns no results. A limited call resets its cached regex so
|
|
1241
|
+
subsequent calls still start at the beginning. Dynamic parse callbacks run to completion and can exceed the
|
|
1242
|
+
requested count; this option cannot limit work inside them. Jira references from this facade are static. Consumers can request
|
|
1243
|
+
one result beyond their display budget to detect overflow, then yield between reference batches to bound
|
|
1244
|
+
work across large catalogs.
|
|
1224
1245
|
|
|
1225
1246
|
¹ Branches in the base repository only: a target whose `headOwner` names another owner is refused, since Bitbucket DC
|
|
1226
1247
|
finds a branch's pull requests only through the repository the branch lives in, and an Azure DevOps fork shares its
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitkraken/core-gitlens",
|
|
3
3
|
"description": "GitLens core — shared Git / AI / GitHub primitives for internal GitKraken consumption",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.22.1",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "GitKraken",
|