@gitkraken/core-gitlens 0.22.1 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -1
- package/dist/plus/integrations/index.d.ts +3 -3
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +10 -2
- package/dist/plus/integrations/integrationService.js +9 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +25 -1
- package/dist/plus/integrations/models/issueReads.d.ts +5 -0
- package/dist/plus/integrations/providers/linear.d.ts +39 -6
- package/dist/plus/integrations/providers/linear.js +129 -15
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +4 -1
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +9 -1
- package/dist/plus/integrations/providers/providersApi.js +8 -0
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/reads/cursors.d.ts +8 -0
- package/dist/plus/integrations/reads/cursors.js +14 -4
- package/dist/plus/integrations/reads/cursors.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.d.ts +8 -2
- package/dist/plus/integrations/reads/hierarchy.js +42 -2
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.d.ts +14 -1
- package/dist/plus/integrations/reads/issueTracker.js +46 -5
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/selectionConfirmation.d.ts +5 -3
- package/dist/plus/integrations/reads/selectionConfirmation.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +1 -1
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +18 -2
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/plus/integrations/utils/integration.utils.d.ts +3 -0
- package/dist/plus/integrations/utils/integration.utils.js +5 -1
- package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
- package/docs/integrations.md +41 -22
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear.js","names":[],"sources":["../../../../../plus/integrations/src/providers/linear.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScopeFailure, GitIssueState } from '@gitkraken/provider-apis';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '@gitlens/git/models/autolink.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 { chunk } from '@gitlens/utils/array.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { mapSettledBounded } from '@gitlens/utils/promise.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { ConnectionResource, ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { IssuesCloudHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport { IntegrationReadUnavailableError } from '../errors.js';\nimport type { IssueEtagFields } from '../models/integration.js';\nimport type {\n\tAccountWideIssuesResult,\n\tIssuesForProjectOptions,\n\tProjectIssuesDrain,\n\tProjectIssuesRequest,\n\tSearchMyIssuesOptions,\n} from '../models/issueReads.js';\nimport {\n\tgroupProjectIssuesSearches,\n\tIssuesIntegration,\n\tsplitProjectIssuesSearch,\n} from '../models/issuesIntegration.js';\nimport type { LinearIssueEtagNode } from './linearIssuesEtag.js';\nimport { linearIssuesEtagMaxNumbers, toLinearIssueEtagFields } from './linearIssuesEtag.js';\nimport type { ProviderApiCollectionResult, ProviderIssue } from './models.js';\nimport { fromProviderIssue, providersMetadata, toIssueShape, toProviderIssueStates } from './models.js';\nimport { DiscoveryCache, discoveryCacheTtl } from './utils/discoveryCache.js';\nimport { createIssueAutolinkReferences } from './utils/issueAutolinks.js';\nimport { mergeCollectionMetadata } from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[IssuesCloudHostIntegrationId.Linear];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\nconst maxPagesPerRequest = 10;\n/**\n * The account-wide drain's own backstop, separate from {@link maxPagesPerRequest}.\n *\n * Raised to 50 (5,000 issues at the SDK's 100-per-page) because 10 no longer means what it used to. That read\n * once fanned out over four overlapping relationship queries and merged them, so most of what a page cost was\n * rows already returned — a low budget capped the waste. It is now a single server-ordered query over an `or`\n * filter, so every page is 100 issues the caller has not seen, and the same budget just truncates real results\n * at 1,000.\n *\n * Reaching it is reported rather than silent: {@link LinearIntegration.searchProviderMyIssuesWithTruncation}\n * returns `truncated`, so the facade can say the read was incomplete instead of publishing a capped list as a\n * whole account. The bound stays — a runaway cursor must not spend requests forever — but it is now set where a\n * real Linear account is unlikely to reach it rather than where the duplication used to hurt.\n */\nconst maxAccountWidePagesPerRequest = 50;\nconst linearImplicitTeamsPageSize = 50;\n/** Long enough to span one tracker read, short enough that a profile change shows on the next. */\nconst recentAccountTTL = 60 * 1000;\n/** A team key and issue number, as Linear writes an issue's identifier (`ENG-123`). */\nconst linearIdentifier = /^([A-Za-z0-9]+)-(\\d+)$/;\n\nexport interface LinearTeamDescriptor extends IssueResourceDescriptor {\n\tavatarUrl: string | undefined;\n}\n\nexport interface LinearOrganizationDescriptor extends IssueResourceDescriptor {\n\turl: string;\n}\n\nexport interface LinearProjectDescriptor extends IssueResourceDescriptor {}\n\nexport class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Linear> {\n\t/**\n\t * Built from the teams, so it follows them: rebuilt when the team list changes or the caches are dropped, and once\n\t * older than {@link discoveryCacheTtl}. An expired set is still served while it is rebuilt in the background, because\n\t * autolinks are read on every render of a commit message and must not wait on the network (#5907).\n\t */\n\tprivate readonly _autolinks = new Map<\n\t\tstring,\n\t\t{ autolinks: (AutolinkReference | DynamicAutolinkReference)[]; builtAt: number }\n\t>();\n\tprivate readonly _autolinksBuilds = new Map<string, Promise<(AutolinkReference | DynamicAutolinkReference)[]>>();\n\n\toverride async autolinks(): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected || this._session == null) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst session = this._session;\n\t\tconst cached = this._autolinks.get(session.accessToken);\n\t\t// A session kept through a failed refresh is expired: building with it would send that token to the provider\n\t\t// on every render, so serve whatever was built before (none after a forced re-sync) until a read refreshes it.\n\t\tif (this.connectionExpired === true) return cached?.autolinks ?? [];\n\t\tif (cached == null) return this.buildAutolinks(session);\n\n\t\tif (Date.now() - cached.builtAt >= discoveryCacheTtl) {\n\t\t\tvoid this.buildAutolinks(session).catch(() => {});\n\t\t}\n\t\treturn cached.autolinks;\n\t}\n\n\t/** One build per token at a time: concurrent renders share it rather than each re-reading the teams. */\n\tprivate buildAutolinks(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst { accessToken } = session;\n\t\tlet build = this._autolinksBuilds.get(accessToken);\n\t\tif (build == null) {\n\t\t\tbuild = this.buildAutolinksCore(session).finally(() => this._autolinksBuilds.delete(accessToken));\n\t\t\tthis._autolinksBuilds.set(accessToken, build);\n\t\t}\n\t\treturn build;\n\t}\n\n\tprivate async buildAutolinksCore(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst generation = this._teams.generation;\n\t\tconst organization = await this.getOrganization(session);\n\t\tif (organization == null) return [];\n\n\t\tconst autolinks: (AutolinkReference | DynamicAutolinkReference)[] = [];\n\n\t\tconst teams = await this.getTeams(session);\n\t\tfor (const team of teams ?? []) {\n\t\t\tautolinks.push(\n\t\t\t\t...createIssueAutolinkReferences(team.key, `${organization.url}/issue/${team.key}-<num>`, organization),\n\t\t\t);\n\t\t}\n\n\t\t// Not kept when built from teams read before the caches were dropped.\n\t\tif (this._teams.generation === generation) {\n\t\t\tthis._autolinks.set(session.accessToken, { autolinks: autolinks, builtAt: Date.now() });\n\t\t}\n\t\treturn autolinks;\n\t}\n\n\t/** The workspace is the token's organization; kept only as a complete, trimmed pair. */\n\toverride async getProviderResourceForSession(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ConnectionResource | undefined> {\n\t\tconst organization = await this.getOrganization(session, true);\n\t\tconst id = organization?.id.trim();\n\t\tconst name = organization?.name.trim();\n\t\treturn id && name ? { id: id, name: name } : undefined;\n\t}\n\n\tprivate _organizations: Map<string, LinearOrganizationDescriptor | undefined> | undefined;\n\t/**\n\t * In flight by token. The request can't be aborted (provider-apis takes no signal), so a lookup that outlives its\n\t * caller's timeout is joined by the next one rather than piling another request on top of it.\n\t */\n\tprivate readonly _organizationRequests = new Map<string, Promise<LinearOrganizationDescriptor | undefined>>();\n\tprivate async getOrganization(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<LinearOrganizationDescriptor | undefined> {\n\t\tconst { accessToken } = session;\n\t\t// Held across the await: a re-sync drops `_organizations` meanwhile, and this read must still complete.\n\t\tconst organizations = (this._organizations ??= new Map<string, LinearOrganizationDescriptor | undefined>());\n\n\t\tconst cachedResources = organizations.get(accessToken);\n\n\t\tif (cachedResources == null || force) {\n\t\t\tlet request = this._organizationRequests.get(accessToken);\n\t\t\tif (request == null) {\n\t\t\t\trequest = this.fetchOrganization(session).finally(() => this._organizationRequests.delete(accessToken));\n\t\t\t\tthis._organizationRequests.set(accessToken, request);\n\t\t\t}\n\n\t\t\tconst descriptor = await request;\n\t\t\tif (descriptor) {\n\t\t\t\torganizations.set(accessToken, descriptor);\n\t\t\t}\n\t\t}\n\n\t\treturn organizations.get(accessToken);\n\t}\n\n\tprivate async fetchOrganization(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<LinearOrganizationDescriptor | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst organization = await api.getLinearOrganization(toTokenWithInfo(this.id, session));\n\t\treturn (\n\t\t\torganization && {\n\t\t\t\tid: organization.id,\n\t\t\t\tkey: organization.key,\n\t\t\t\tname: organization.name,\n\t\t\t\turl: organization.url,\n\t\t\t}\n\t\t);\n\t}\n\n\t/** Expires, and is dropped on a re-sync, so a team created or left mid-session is picked up (#5907). */\n\tprivate readonly _teams = new DiscoveryCache<LinearTeamDescriptor[]>();\n\tprivate async getTeams(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<LinearTeamDescriptor[] | undefined> {\n\t\treturn (await this.getTeamsWithMetadata(session, force))?.values;\n\t}\n\n\tprivate async getTeamsWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<ProviderApiCollectionResult<LinearTeamDescriptor> | undefined> {\n\t\tconst { accessToken } = session;\n\n\t\tconst cachedResources = this._teams.get(accessToken);\n\t\tif (cachedResources != null && !force) return { values: cachedResources };\n\n\t\tconst generation = this._teams.generation;\n\t\tconst read = await this.readTeams(session);\n\t\tif (read?.metadata != null) return read;\n\n\t\tif (read != null && this._teams.set(accessToken, read.values, { generation: generation })) {\n\t\t\t// Built from the teams; a new set means new prefixes.\n\t\t\tthis._autolinks.delete(accessToken);\n\t\t}\n\t\treturn read;\n\t}\n\n\tprivate async readTeams(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderApiCollectionResult<LinearTeamDescriptor> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst teams = await api.getLinearTeamsForCurrentUser(toTokenWithInfo(this.id, session));\n\t\tconst descriptors: LinearTeamDescriptor[] | undefined = teams?.map(t => ({\n\t\t\tid: t.id,\n\t\t\tkey: t.key,\n\t\t\tname: t.name,\n\t\t\tavatarUrl: t.iconUrl,\n\t\t}));\n\t\tif (descriptors == null) return undefined;\n\n\t\t// provider-apis currently requests Linear's teams connection without pageInfo or a cursor. Linear's\n\t\t// implicit page size is 50, so exactly a full page cannot prove there is no team 51. Preserve the useful\n\t\t// prefix, but mark it unknown and leave it uncached so the next read retries instead of treating it as\n\t\t// authoritative. Once provider-apis exposes a paged primitive, replace this fail-closed guard with a drain.\n\t\tif (descriptors.length >= linearImplicitTeamsPageSize) {\n\t\t\treturn { values: descriptors, metadata: { completeness: 'unknown' } };\n\t\t}\n\n\t\treturn { values: descriptors };\n\t}\n\n\tprotected override async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\tconst organization = await this.getOrganization(session);\n\t\treturn organization != null ? [organization] : undefined;\n\t}\n\tprotected override async getProviderUncachedResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\t// Joins none in flight, unlike `getOrganization`: one may have started before what the caller needs to see.\n\t\tconst organization = await this.fetchOrganization(session);\n\t\treturn organization != null ? [organization] : undefined;\n\t}\n\tprotected override getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\treturn this.getTeams(session);\n\t}\n\tprotected override async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ProviderApiCollectionResult<ResourceDescriptor>> {\n\t\treturn (await this.getTeamsWithMetadata(session)) ?? { values: [], metadata: { completeness: 'unknown' } };\n\t}\n\tprotected override async getProviderUncachedProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ProviderApiCollectionResult<ResourceDescriptor>> {\n\t\treturn (await this.readTeams(session)) ?? { values: [], metadata: { completeness: 'unknown' } };\n\t}\n\n\tprotected override clearDiscoveryCaches(): void {\n\t\tthis._organizations = undefined;\n\t\tthis._teams.clear();\n\t\tthis._autolinks.clear();\n\t\tthis._recentAccounts.clear();\n\t}\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\n\t/**\n\t * Always asks Linear: the host's current-account cache decides when this runs. The viewer query is account-wide,\n\t * so the answer is lent briefly to the per-resource 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);\n\t}\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\treturn this._recentAccounts.getOrCreate(session.accessToken, () => this.fetchAccount(session), {\n\t\t\tevictWhen: account => account == null,\n\t\t});\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(session: ProviderAuthenticationSession): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// Linear's viewer isn't a ProviderAccount (no username/avatar), so build the Account manually\n\t\t// (Trello-style) from the fields the viewer query returns.\n\t\tconst user = await api.getLinearCurrentUser(toTokenWithInfo(this.id, session));\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 ?? user.displayName ?? undefined,\n\t\t\tusername: user.displayName ?? undefined,\n\t\t\temail: user.email ?? undefined,\n\t\t\tavatarUrl: undefined,\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\tif (!isIssueResourceDescriptor(project)) return undefined;\n\n\t\t// Scope to \"my issues\" by the viewer's stable id, not the passed display name: Linear's `name` (full name)\n\t\t// and `displayName` (nickname) are distinct fields, and assignees are normalized with `.name` = the full\n\t\t// name while the caller's `user` is the displayName — so a name string can miss. The assignee `.id` is the\n\t\t// Linear user id, which is unambiguous.\n\t\tlet viewerId: string | undefined;\n\t\tif (options?.user != null) {\n\t\t\tviewerId = await this.getViewerId(session, options);\n\t\t\t// If the viewer can't be resolved we can't scope to \"my issues\" — returning the unfiltered team\n\t\t\t// issues would leak everyone else's, and returning [] is indistinguishable from \"no issues assigned\n\t\t\t// to me\". Throw so the facade (getIssuesForProjectResult → runCaptured) surfaces a warning +\n\t\t\t// fetchFailed the caller can act on, instead of a silent empty.\n\t\t\tif (viewerId == null) {\n\t\t\t\tthrow new IntegrationReadUnavailableError(\n\t\t\t\t\tmetadata.name,\n\t\t\t\t\t'could not resolve the current user to scope issues to',\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// `getProviderProjectsForResources` returns Linear teams, so `project.id` is a team id here.\n\t\t//\n\t\t// The state and the viewer are narrowed server-side, not after the drain: a team's done work and other\n\t\t// people's issues would otherwise count against the backstop, so a busy team runs out of pages before the\n\t\t// viewer's open issues and hands back a page that looks complete.\n\t\tconst drain = await this.drainIssues(\n\t\t\tsession,\n\t\t\t{\n\t\t\t\tteams: [project.id],\n\t\t\t\t...(viewerId != null ? { assignees: [viewerId] } : {}),\n\t\t\t\tstates: toProviderIssueStates(options?.state),\n\t\t\t},\n\t\t\toptions?.sort,\n\t\t\t{ providerId: this.id, projectId: project.id },\n\t\t);\n\n\t\treturn drain.truncated\n\t\t\t? { values: drain.issues, truncated: true, recovery: 'none', metadata: drain.metadata }\n\t\t\t: { values: drain.issues, truncated: false, metadata: drain.metadata };\n\t}\n\n\t/**\n\t * Searches every team of a user-scoped read together: one `getIssues` names all of them and the viewer as\n\t * assignee, so a team with none of the user's issues costs no request of its own. Linear's filter is a GraphQL\n\t * body, not a query string, so there is no key bound to chunk at.\n\t */\n\tprotected override getProjectIssuesSearches(\n\t\trequests: readonly ProjectIssuesRequest<ResourceDescriptor>[],\n\t): number[][] | undefined {\n\t\treturn groupProjectIssuesSearches(requests, ({ project, options }) =>\n\t\t\toptions.user != null && isIssueResourceDescriptor(project)\n\t\t\t\t? [options.user, options.userId ?? '', options.sort ?? '', options.state ?? ''].join('\\0')\n\t\t\t\t: undefined,\n\t\t);\n\t}\n\n\tprotected override async searchProviderProjectIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trequests: readonly ProjectIssuesRequest<ResourceDescriptor>[],\n\t): Promise<IssueShape[][] | undefined> {\n\t\t// Every request of a search shares its user scope (see `getProjectIssuesSearches`).\n\t\tconst { options } = requests[0];\n\t\tif (options.user == null) return undefined;\n\n\t\t// Unresolvable here means each team's own read throws the same warning, so let them.\n\t\tconst viewerId = await this.getViewerId(session, options);\n\t\tif (viewerId == null) return undefined;\n\n\t\tconst teams = requests.map(request => request.project).filter(isIssueResourceDescriptor);\n\t\tif (teams.length !== requests.length) return undefined;\n\n\t\tconst drain = await this.drainIssues(\n\t\t\tsession,\n\t\t\t{\n\t\t\t\tteams: teams.map(team => team.id),\n\t\t\t\tassignees: [viewerId],\n\t\t\t\tstates: toProviderIssueStates(options.state),\n\t\t\t},\n\t\t\toptions.sort,\n\t\t\t// Never published: an incomplete search is read again per team, which records its own failures.\n\t\t\t{ providerId: this.id },\n\t\t);\n\t\t// Deliberately discarded rather than served: an incomplete search says nothing about which of its teams it\n\t\t// covered, so only the per-team reads can report each one's completeness. The waste is bounded by one team's\n\t\t// page budget, and paid only by a search that needed more than that or failed partway.\n\t\tif (drain.truncated || (drain.metadata != null && drain.metadata.completeness !== 'complete')) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// A normalized Linear issue carries its team only inside `project`, which is absent for an issue in no\n\t\t// Linear project, so split by the identifier instead: Linear numbers every issue `<team key>-<n>`, and\n\t\t// renumbers it under its new team's key when it moves.\n\t\treturn splitProjectIssuesSearch(\n\t\t\tteams.map(team => team.key),\n\t\t\tdrain.issues,\n\t\t\tissue => {\n\t\t\t\tconst separator = issue.id.lastIndexOf('-');\n\t\t\t\treturn separator > 0 ? issue.id.slice(0, separator) : undefined;\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate readonly _viewerIds = new PromiseCache<string, string | undefined>({ capacity: 10 });\n\t/**\n\t * The viewer's Linear user id: the caller's resolved `userId` when it has one, else the viewer query, shared per\n\t * token. A tracker read hands each team the id of the team's own resource, which Linear's team descriptors don't\n\t * name, so without sharing every team read (and a search's fallback) would ask for the viewer again.\n\t */\n\tprivate getViewerId(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: IssuesForProjectOptions,\n\t): Promise<string | undefined> {\n\t\tif (options.userId) return Promise.resolve(options.userId);\n\n\t\treturn this._viewerIds.getOrCreate(\n\t\t\tsession.accessToken,\n\t\t\tasync () => {\n\t\t\t\tconst api = await this.getProvidersApi();\n\t\t\t\treturn (await api.getLinearCurrentUser(toTokenWithInfo(this.id, session)))?.id;\n\t\t\t},\n\t\t\t// Only a resolved id is worth keeping; an unresolved viewer is asked for again next time, as is a failure.\n\t\t\t{ evictWhen: id => id == null },\n\t\t);\n\t}\n\n\t/**\n\t * Follows `getIssues`' cursor up to {@link maxPagesPerRequest}. `truncated` is set when that backstop stopped\n\t * the drain with more pages still available, when the provider stalled its cursor or flagged its paging as\n\t * truncated, or when a page after the first failed, which is recorded at `failureScope` while the prefix is\n\t * kept. A first-page failure throws, so the caller sees a hard error rather than an empty success.\n\t */\n\tprivate async drainIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tfilter: { teams: string[]; assignees?: string[]; states?: GitIssueState[] },\n\t\tsort: IssuesForProjectOptions['sort'],\n\t\tfailureScope: CollectionScopeFailure['scope'],\n\t): Promise<{ issues: IssueShape[]; truncated: boolean; metadata?: CollectionMetadata }> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst assignees = filter.assignees?.length ? filter.assignees : undefined;\n\t\tlet cursor: string | undefined;\n\t\tlet hasMore: boolean;\n\t\tlet requestCount = 0;\n\t\tlet truncated = false;\n\t\tlet collectionMetadata: CollectionMetadata | undefined;\n\t\tconst issues: IssueShape[] = [];\n\t\tdo {\n\t\t\tlet result: Awaited<ReturnType<typeof api.getLinearIssues>>;\n\t\t\ttry {\n\t\t\t\tresult = await api.getLinearIssues(toTokenWithInfo(this.id, session), filter, {\n\t\t\t\t\tcursor: cursor,\n\t\t\t\t\tsort: sort,\n\t\t\t\t});\n\t\t\t} catch (ex) {\n\t\t\t\tif (issues.length === 0) throw ex;\n\n\t\t\t\ttruncated = true;\n\t\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, {\n\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\tfailures: [toCollectionScopeFailure(failureScope, ex)],\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trequestCount += 1;\n\t\t\thasMore = result.paging?.more ?? false;\n\t\t\tconst nextCursor = result.paging?.cursor;\n\t\t\ttruncated ||= result.paging?.truncated === true;\n\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, result.metadata);\n\t\t\tfor (const issue of result.values) {\n\t\t\t\tconst shape = toIssueShape(issue, this, { projection: 'project' });\n\t\t\t\t// The query already scopes to the assignees; checking again keeps another user's issue out of \"my\n\t\t\t\t// issues\" should the provider ever return one.\n\t\t\t\tif (shape != null && (assignees == null || shape.assignees?.some(a => assignees.includes(a.id)))) {\n\t\t\t\t\tissues.push(shape);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The provider claims more but returns no advancing cursor: we can't continue without re-reading the\n\t\t\t// same page, so the drain is incomplete — flag it rather than silently stopping.\n\t\t\tif (hasMore && (nextCursor == null || nextCursor === cursor)) {\n\t\t\t\ttruncated = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcursor = nextCursor;\n\t\t\tif (hasMore && requestCount >= maxPagesPerRequest) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t} while (requestCount < maxPagesPerRequest && hasMore);\n\n\t\treturn { issues: issues, truncated: truncated, metadata: collectionMetadata };\n\t}\n\n\toverride get id(): IssuesCloudHostIntegrationId.Linear {\n\t\treturn IssuesCloudHostIntegrationId.Linear;\n\t}\n\tprotected override get key(): 'linear' {\n\t\treturn 'linear';\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\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, resources, cancellation))?.values;\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for Linear, drained to exhaustion and reporting whether it got there.\n\t *\n\t * The SDK read is one server-ordered query over an `or` filter across the four relationships (assigned,\n\t * created, named in an issue's content, named in a comment), so each page is 100 issues the caller has not\n\t * seen and the pages are ordered as one sequence. Draining it is therefore just following the cursor.\n\t *\n\t * Overridden rather than left to the base default because that default hardcodes `truncated: false`, and a\n\t * bounded drain that stops early is exactly the case a caller must be able to see. Three things end the\n\t * drain, and only one of them is completion:\n\t *\n\t * - the provider says there is no next page — complete;\n\t * - the backstop is reached — `truncated`, because issues beyond it exist and were not read;\n\t * - the provider claims a next page but hands back a cursor that does not advance — also `truncated`, since\n\t * the read cannot continue and the remainder is unreachable rather than absent.\n\t *\n\t * Cancellation is deliberately NOT truncation: the caller asked for the read to stop, so the partial list is\n\t * what it asked for, and flagging it would surface an incompleteness warning for a user action.\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tif (resources != null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst states = toProviderIssueStates(options?.state);\n\t\tlet cursor = undefined;\n\t\t// Starts false so an immediate cancellation, which leaves the loop before the first response, reads as\n\t\t// \"no more pages known\" rather than as an unfinished drain.\n\t\tlet hasMore = false;\n\t\tlet requestCount = 0;\n\t\tlet truncated = false;\n\t\tconst issues = [];\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tif (cancellation?.aborted) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst result = await api.getIssuesForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\t\t\tcursor: cursor,\n\t\t\t\t\tstates: states,\n\t\t\t\t\tsort: options?.sort,\n\t\t\t\t});\n\t\t\t\trequestCount += 1;\n\t\t\t\thasMore = result.paging?.more ?? false;\n\t\t\t\tconst nextCursor = result.paging?.cursor;\n\n\t\t\t\t// Keep this page before deciding whether to continue: the request is already paid for, so\n\t\t\t\t// dropping its rows would lose real results to save nothing.\n\t\t\t\tconst formattedIssues = result.values\n\t\t\t\t\t.map(issue => toIssueShape(issue, this, { projection: 'account' }))\n\t\t\t\t\t.filter((result): result is IssueShape => result != null);\n\t\t\t\tif (formattedIssues.length > 0) {\n\t\t\t\t\tissues.push(...formattedIssues);\n\t\t\t\t}\n\n\t\t\t\t// The provider claims more but hands back no advancing cursor: continuing would re-read the page\n\t\t\t\t// just returned. `ProvidersApi.getPagedResult` already normalizes this into `more: false`, so\n\t\t\t\t// this is a backstop for if that ever stops holding rather than a case seen today -- but the rest\n\t\t\t\t// of the account is unreachable either way, which is what `truncated` says.\n\t\t\t\tif (hasMore && (nextCursor == null || nextCursor === cursor)) {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcursor = nextCursor;\n\t\t\t} while (requestCount < maxAccountWidePagesPerRequest && hasMore);\n\n\t\t\t// Ran out of budget with pages still to come.\n\t\t\tif (hasMore && requestCount >= maxAccountWidePagesPerRequest) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (issues.length === 0) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\t// Kept what was already fetched, so the list is real but short of the account.\n\t\t\ttruncated = true;\n\t\t\tLogger.error(ex, 'searchProviderMyIssues');\n\t\t}\n\t\treturn { values: issues, truncated: truncated };\n\t}\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\t{ key }: { id: string; key: string },\n\t\t_type: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst issue = await this.getRawProviderIssue(session, resource, key);\n\t\tconst autolinkableIssue: ProviderIssue | undefined = issue && {\n\t\t\t...issue,\n\t\t\turl: this.getIssueAutolinkLikeUrl(issue),\n\t\t};\n\t\treturn autolinkableIssue && toIssueShape(autolinkableIssue, this, { projection: 'point' });\n\t}\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst result = await this.getRawProviderIssue(session, resource, id);\n\t\treturn result && fromProviderIssue(result, this, { projection: 'point' });\n\t}\n\n\tprotected override async getProviderIssueByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresourceId: string,\n\t\tid: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst result = await api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resourceId,\n\t\t\tnumber: id,\n\t\t});\n\t\treturn result && fromProviderIssue(result, this, { projection: 'batch' });\n\t}\n\n\t/**\n\t * The cheap check behind the batch issue read's etags: one `issues` query per team and\n\t * {@link linearIssuesEtagMaxNumbers} distinct numbers, where {@link getProviderIssueByResourceId} sends one request\n\t * per issue. A request that throws rejects only its own targets' slots, with its error classified as the full\n\t * read's would be.\n\t *\n\t * Never proves an absence. The query filters by the team's CURRENT key and asks for archived issues too, but the\n\t * full read's `issue(id:)` may also resolve an identifier the issue no longer carries (its team was renamed, or it\n\t * moved to another team), so an issue the query leaves out may still exist. Its target's slot is rejected, as is a\n\t * target whose identifier isn't a team key and a number, so the full read decides. When no target was answered and\n\t * nothing failed, the check declines instead, so a batch of only such targets costs a full read, not a failure.\n\t */\n\tprotected override async getProviderIssuesEtagFieldsByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined> {\n\t\tconst slots = new Array<PromiseSettledResult<IssueEtagFields | undefined>>(targets.length);\n\t\tlet answered = false;\n\t\tlet failed = false;\n\n\t\t// Each workspace and team's distinct numbers, with the targets asking for each.\n\t\tconst byTeam = new Map<string, { teamKey: string; numbers: Map<number, number[]> }>();\n\t\tfor (const [index, target] of targets.entries()) {\n\t\t\tconst match = linearIdentifier.exec(target.identifier);\n\t\t\tconst number = match != null ? Number(match[2]) : Number.NaN;\n\t\t\t// Only a number written as Linear writes it, so `ENG-012` never answers for `ENG-12`.\n\t\t\tif (match == null || !Number.isSafeInteger(number) || String(number) !== match[2]) {\n\t\t\t\tslots[index] = {\n\t\t\t\t\tstatus: 'rejected',\n\t\t\t\t\treason: new Error(`Not a Linear issue identifier: ${target.identifier}`),\n\t\t\t\t};\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst teamKey = match[1].toUpperCase();\n\t\t\tconst groupKey = `${target.resourceId}\\n${teamKey}`;\n\t\t\tlet group = byTeam.get(groupKey);\n\t\t\tif (group == null) {\n\t\t\t\tgroup = { teamKey: teamKey, numbers: new Map() };\n\t\t\t\tbyTeam.set(groupKey, group);\n\t\t\t}\n\n\t\t\tlet indices = group.numbers.get(number);\n\t\t\tif (indices == null) {\n\t\t\t\tindices = [];\n\t\t\t\tgroup.numbers.set(number, indices);\n\t\t\t}\n\t\t\tindices.push(index);\n\t\t}\n\n\t\tconst requests = [...byTeam.values()].flatMap(({ teamKey, numbers }) =>\n\t\t\tchunk([...numbers], linearIssuesEtagMaxNumbers).map(entries => ({ teamKey: teamKey, entries: entries })),\n\t\t);\n\t\tif (requests.length) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t\tconst answers = await mapSettledBounded(requests, providerFanOutConcurrency, r =>\n\t\t\t\tapi.getLinearIssuesEtagFields(\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\tr.teamKey,\n\t\t\t\t\tr.entries.map(([number]) => number),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tfor (const [i, answer] of answers.entries()) {\n\t\t\t\tconst { teamKey, entries } = requests[i];\n\t\t\t\tif (answer.status === 'rejected') {\n\t\t\t\t\tfailed = true;\n\t\t\t\t\tfor (const [, indices] of entries) {\n\t\t\t\t\t\tfor (const index of indices) {\n\t\t\t\t\t\t\tslots[index] = answer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// By number, never by position: Linear returns the issues in its own order, and only the ones it found.\n\t\t\t\tconst byNumber = new Map<number, LinearIssueEtagNode>();\n\t\t\t\tfor (const issue of answer.value) {\n\t\t\t\t\tbyNumber.set(issue.number, issue);\n\t\t\t\t}\n\n\t\t\t\tfor (const [number, indices] of entries) {\n\t\t\t\t\tconst issue = byNumber.get(number);\n\t\t\t\t\tlet slot: PromiseSettledResult<IssueEtagFields | undefined>;\n\t\t\t\t\tif (issue == null) {\n\t\t\t\t\t\tslot = { status: 'rejected', reason: new Error(`Linear did not return ${teamKey}-${number}`) };\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tslot = { status: 'fulfilled', value: toLinearIssueEtagFields(issue) };\n\t\t\t\t\t\t\tanswered = true;\n\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\tslot = { status: 'rejected', reason: ex };\n\t\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const index of indices) {\n\t\t\t\t\t\tslots[index] = slot;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn answered || failed ? slots : undefined;\n\t}\n\n\tprivate async getRawProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<ProviderIssue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif (!isIssueResourceDescriptor(resource)) {\n\t\t\tLogger.error(undefined, 'getProviderIssue: resource is not an IssueResourceDescriptor');\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resource.id,\n\t\t\tnumber: id,\n\t\t});\n\t}\n\tprivate getIssueAutolinkLikeUrl(issue: ProviderIssue): string | null {\n\t\tconst url = issue.url;\n\t\tif (url == null) return null;\n\n\t\tconst lastSegment = url.split('/').pop();\n\t\tif (!lastSegment || issue.number === lastSegment) {\n\t\t\treturn url;\n\t\t}\n\t\treturn url.substring(0, url.length - lastSegment.length - 1);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"linear.js","names":[],"sources":["../../../../../plus/integrations/src/providers/linear.ts"],"sourcesContent":["import type { CollectionMetadata, CollectionScopeFailure, GitIssueState } from '@gitkraken/provider-apis';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '@gitlens/git/models/autolink.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 { chunk } from '@gitlens/utils/array.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { mapSettledBounded } from '@gitlens/utils/promise.js';\nimport { PromiseCache } from '@gitlens/utils/promiseCache.js';\nimport type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider.js';\nimport type { ConnectionResource, ProviderAuthenticationSession } from '../authentication/models.js';\nimport { toTokenWithInfo } from '../authentication/models.js';\nimport { toCollectionScopeFailure } from '../collectionMetadata.js';\nimport { IssuesCloudHostIntegrationId, providerFanOutConcurrency } from '../constants.js';\nimport { IntegrationReadUnavailableError, toError } from '../errors.js';\nimport type { IntegrationResult, IssueEtagFields } from '../models/integration.js';\nimport { isReadSessionFailure } from '../models/integration.js';\nimport type {\n\tAccountWideIssuesResult,\n\tIssuesForProjectOptions,\n\tProjectIssuesDrain,\n\tProjectIssuesRequest,\n\tSearchMyIssuesOptions,\n} from '../models/issueReads.js';\nimport {\n\tgroupProjectIssuesSearches,\n\tIssuesIntegration,\n\tsplitProjectIssuesSearch,\n} from '../models/issuesIntegration.js';\nimport { IssueFilter } from '../providerFilters.js';\nimport type { LinearIssueEtagNode } from './linearIssuesEtag.js';\nimport { linearIssuesEtagMaxNumbers, toLinearIssueEtagFields } from './linearIssuesEtag.js';\nimport type {\n\tProviderApiCollectionResult,\n\tProviderHierarchyResult,\n\tProviderIssue,\n\tProviderLinearProject,\n} from './models.js';\nimport { fromProviderIssue, providersMetadata, toIssueShape, toProviderIssueStates } from './models.js';\nimport { DiscoveryCache, discoveryCacheTtl } from './utils/discoveryCache.js';\nimport { createIssueAutolinkReferences } from './utils/issueAutolinks.js';\nimport { collectProviderPagedResult, mergeCollectionMetadata } from './utils/providerPaging.js';\n\nconst metadata = providersMetadata[IssuesCloudHostIntegrationId.Linear];\nconst authProvider = Object.freeze({ id: metadata.id, scopes: metadata.scopes });\nconst maxPagesPerRequest = 10;\n/**\n * The account-wide drain's own backstop, separate from {@link maxPagesPerRequest}.\n *\n * Raised to 50 (5,000 issues at the SDK's 100-per-page) because 10 no longer means what it used to. That read\n * once fanned out over four overlapping relationship queries and merged them, so most of what a page cost was\n * rows already returned — a low budget capped the waste. It is now a single server-ordered query over an `or`\n * filter, so every page is 100 issues the caller has not seen, and the same budget just truncates real results\n * at 1,000.\n *\n * Reaching it is reported rather than silent: {@link LinearIntegration.searchProviderMyIssuesWithTruncation}\n * returns `truncated`, so the facade can say the read was incomplete instead of publishing a capped list as a\n * whole account. The bound stays — a runaway cursor must not spend requests forever — but it is now set where a\n * real Linear account is unlikely to reach it rather than where the duplication used to hurt.\n */\nconst maxAccountWidePagesPerRequest = 50;\nconst linearImplicitTeamsPageSize = 50;\n/** The workspace Projects read's backstop: provider-apis pages them 100 at a time, so 2,000 Projects. */\nconst maxWorkspaceProjectPages = 20;\n/** Long enough to span one tracker read, short enough that a profile change shows on the next. */\nconst recentAccountTTL = 60 * 1000;\n/** A team key and issue number, as Linear writes an issue's identifier (`ENG-123`). */\nconst linearIdentifier = /^([A-Za-z0-9]+)-(\\d+)$/;\n\nexport interface LinearTeamDescriptor extends IssueResourceDescriptor {\n\tavatarUrl: string | undefined;\n}\n\nexport interface LinearOrganizationDescriptor extends IssueResourceDescriptor {\n\turl: string;\n}\n\nexport interface LinearProjectDescriptor extends IssueResourceDescriptor {}\n\n/** The part of a team read's filter that narrows it to the viewer. */\ntype LinearViewerScope = { assignees: string[]; githubSyncedCreators?: string[] };\n\n/**\n * How a user-scoped team read narrows to the viewer: the issues assigned to them, plus, unless the caller asked for\n * assigned issues by name, the GitHub-synced issues they created. Linear's GitHub Issues sync makes the GitHub author\n * of an issue (one who linked their GitHub account) the creator of its Linear copy but assigns no one GitHub doesn't,\n * so an assignee-only read never returns the copy of a GitHub issue the user filed (gitkraken/kepler#4480). Only\n * synced issues widen, and only those the viewer created on either side: the rest of what the user created in Linear\n * stays out of \"my issues\", as does a synced issue they merely follow.\n */\nfunction viewerScope(viewerId: string, filters: IssueFilter[] | undefined): LinearViewerScope {\n\treturn filters?.includes(IssueFilter.Assignee)\n\t\t? { assignees: [viewerId] }\n\t\t: { assignees: [viewerId], githubSyncedCreators: [viewerId] };\n}\n\nexport class LinearIntegration extends IssuesIntegration<IssuesCloudHostIntegrationId.Linear> {\n\t/**\n\t * Built from the teams, so it follows them: rebuilt when the team list changes or the caches are dropped, and once\n\t * older than {@link discoveryCacheTtl}. An expired set is still served while it is rebuilt in the background, because\n\t * autolinks are read on every render of a commit message and must not wait on the network (#5907).\n\t */\n\tprivate readonly _autolinks = new Map<\n\t\tstring,\n\t\t{ autolinks: (AutolinkReference | DynamicAutolinkReference)[]; builtAt: number }\n\t>();\n\tprivate readonly _autolinksBuilds = new Map<string, Promise<(AutolinkReference | DynamicAutolinkReference)[]>>();\n\n\toverride async autolinks(): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst connected = this.maybeConnected ?? (await this.isConnected());\n\t\tif (!connected || this._session == null) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst session = this._session;\n\t\tconst cached = this._autolinks.get(session.accessToken);\n\t\t// A session kept through a failed refresh is expired: building with it would send that token to the provider\n\t\t// on every render, so serve whatever was built before (none after a forced re-sync) until a read refreshes it.\n\t\tif (this.connectionExpired === true) return cached?.autolinks ?? [];\n\t\tif (cached == null) return this.buildAutolinks(session);\n\n\t\tif (Date.now() - cached.builtAt >= discoveryCacheTtl) {\n\t\t\tvoid this.buildAutolinks(session).catch(() => {});\n\t\t}\n\t\treturn cached.autolinks;\n\t}\n\n\t/** One build per token at a time: concurrent renders share it rather than each re-reading the teams. */\n\tprivate buildAutolinks(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst { accessToken } = session;\n\t\tlet build = this._autolinksBuilds.get(accessToken);\n\t\tif (build == null) {\n\t\t\tbuild = this.buildAutolinksCore(session).finally(() => this._autolinksBuilds.delete(accessToken));\n\t\t\tthis._autolinksBuilds.set(accessToken, build);\n\t\t}\n\t\treturn build;\n\t}\n\n\tprivate async buildAutolinksCore(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<(AutolinkReference | DynamicAutolinkReference)[]> {\n\t\tconst generation = this._teams.generation;\n\t\tconst organization = await this.getOrganization(session);\n\t\tif (organization == null) return [];\n\n\t\tconst autolinks: (AutolinkReference | DynamicAutolinkReference)[] = [];\n\n\t\tconst teams = await this.getTeams(session);\n\t\tfor (const team of teams ?? []) {\n\t\t\tautolinks.push(\n\t\t\t\t...createIssueAutolinkReferences(team.key, `${organization.url}/issue/${team.key}-<num>`, organization),\n\t\t\t);\n\t\t}\n\n\t\t// Not kept when built from teams read before the caches were dropped.\n\t\tif (this._teams.generation === generation) {\n\t\t\tthis._autolinks.set(session.accessToken, { autolinks: autolinks, builtAt: Date.now() });\n\t\t}\n\t\treturn autolinks;\n\t}\n\n\t/** The workspace is the token's organization; kept only as a complete, trimmed pair. */\n\toverride async getProviderResourceForSession(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ConnectionResource | undefined> {\n\t\tconst organization = await this.getOrganization(session, true);\n\t\tconst id = organization?.id.trim();\n\t\tconst name = organization?.name.trim();\n\t\treturn id && name ? { id: id, name: name } : undefined;\n\t}\n\n\tprivate _organizations: Map<string, LinearOrganizationDescriptor | undefined> | undefined;\n\t/**\n\t * In flight by token. The request can't be aborted (provider-apis takes no signal), so a lookup that outlives its\n\t * caller's timeout is joined by the next one rather than piling another request on top of it.\n\t */\n\tprivate readonly _organizationRequests = new Map<string, Promise<LinearOrganizationDescriptor | undefined>>();\n\tprivate async getOrganization(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<LinearOrganizationDescriptor | undefined> {\n\t\tconst { accessToken } = session;\n\t\t// Held across the await: a re-sync drops `_organizations` meanwhile, and this read must still complete.\n\t\tconst organizations = (this._organizations ??= new Map<string, LinearOrganizationDescriptor | undefined>());\n\n\t\tconst cachedResources = organizations.get(accessToken);\n\n\t\tif (cachedResources == null || force) {\n\t\t\tlet request = this._organizationRequests.get(accessToken);\n\t\t\tif (request == null) {\n\t\t\t\trequest = this.fetchOrganization(session).finally(() => this._organizationRequests.delete(accessToken));\n\t\t\t\tthis._organizationRequests.set(accessToken, request);\n\t\t\t}\n\n\t\t\tconst descriptor = await request;\n\t\t\tif (descriptor) {\n\t\t\t\torganizations.set(accessToken, descriptor);\n\t\t\t}\n\t\t}\n\n\t\treturn organizations.get(accessToken);\n\t}\n\n\tprivate async fetchOrganization(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<LinearOrganizationDescriptor | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst organization = await api.getLinearOrganization(toTokenWithInfo(this.id, session));\n\t\treturn (\n\t\t\torganization && {\n\t\t\t\tid: organization.id,\n\t\t\t\tkey: organization.key,\n\t\t\t\tname: organization.name,\n\t\t\t\turl: organization.url,\n\t\t\t}\n\t\t);\n\t}\n\n\t/** Expires, and is dropped on a re-sync, so a team created or left mid-session is picked up (#5907). */\n\tprivate readonly _teams = new DiscoveryCache<LinearTeamDescriptor[]>();\n\tprivate async getTeams(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<LinearTeamDescriptor[] | undefined> {\n\t\treturn (await this.getTeamsWithMetadata(session, force))?.values;\n\t}\n\n\tprivate async getTeamsWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\tforce: boolean = false,\n\t): Promise<ProviderApiCollectionResult<LinearTeamDescriptor> | undefined> {\n\t\tconst { accessToken } = session;\n\n\t\tconst cachedResources = this._teams.get(accessToken);\n\t\tif (cachedResources != null && !force) return { values: cachedResources };\n\n\t\tconst generation = this._teams.generation;\n\t\tconst read = await this.readTeams(session);\n\t\tif (read?.metadata != null) return read;\n\n\t\tif (read != null && this._teams.set(accessToken, read.values, { generation: generation })) {\n\t\t\t// Built from the teams; a new set means new prefixes.\n\t\t\tthis._autolinks.delete(accessToken);\n\t\t}\n\t\treturn read;\n\t}\n\n\tprivate async readTeams(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderApiCollectionResult<LinearTeamDescriptor> | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst teams = await api.getLinearTeamsForCurrentUser(toTokenWithInfo(this.id, session));\n\t\tconst descriptors: LinearTeamDescriptor[] | undefined = teams?.map(t => ({\n\t\t\tid: t.id,\n\t\t\tkey: t.key,\n\t\t\tname: t.name,\n\t\t\tavatarUrl: t.iconUrl,\n\t\t}));\n\t\tif (descriptors == null) return undefined;\n\n\t\t// provider-apis currently requests Linear's teams connection without pageInfo or a cursor. Linear's\n\t\t// implicit page size is 50, so exactly a full page cannot prove there is no team 51. Preserve the useful\n\t\t// prefix, but mark it unknown and leave it uncached so the next read retries instead of treating it as\n\t\t// authoritative. Once provider-apis exposes a paged primitive, replace this fail-closed guard with a drain.\n\t\tif (descriptors.length >= linearImplicitTeamsPageSize) {\n\t\t\treturn { values: descriptors, metadata: { completeness: 'unknown' } };\n\t\t}\n\n\t\treturn { values: descriptors };\n\t}\n\n\t/**\n\t * The workspace's Linear Projects (Linear's `Project` entity), drained across their pages. Separate from the\n\t * teams this integration lists as its projects: a Linear Project can span several teams, and selecting one\n\t * narrows an issue read (`IssuesForProjectOptions.linearProjects`) rather than naming what is read.\n\t *\n\t * Cached like the teams, and only when complete: a drain cut short by its backstop or a failed page is returned\n\t * with its `truncated`/`metadata` so the facade can say so, and read again next time. A first-page failure\n\t * rejects, which the result wrapper turns into `{ error }`. `fresh` reads past the cache and any read in flight,\n\t * for a caller that must not take a list from before what it needs to see (a Project created since).\n\t */\n\t@trace()\n\tasync getWorkspaceProjectsResult(\n\t\tconnectionId?: string,\n\t\toptions?: { fresh?: boolean },\n\t): Promise<IntegrationResult<ProviderHierarchyResult<ProviderLinearProject>>> {\n\t\tconst scope = getScopedLogger();\n\t\t// `connectionId` targets a specific account (multi-account); omitted reads the primary.\n\t\tconst session = await this.resolveReadSession(connectionId, scope);\n\t\tif (session == null || isReadSessionFailure(session)) return session && { error: session.error };\n\n\t\ttry {\n\t\t\tconst projects = await this.getWorkspaceProjects(session, options?.fresh === true);\n\t\t\tawait this.confirmScopedAuthFailures(session, projects.metadata);\n\t\t\tthis.resetRequestExceptionCount('getProjectsForResources');\n\t\t\treturn { value: projects };\n\t\t} catch (ex) {\n\t\t\tthis.handleProviderException('getProjectsForResources', ex, { connectionId: connectionId });\n\t\t\treturn { error: toError(ex) };\n\t\t}\n\t}\n\n\t/** Expires, and is dropped on a re-sync, so a Project created or archived mid-session is picked up. */\n\tprivate readonly _workspaceProjects = new DiscoveryCache<ProviderLinearProject[]>();\n\t/**\n\t * In flight by token, with the cache generation it started under: concurrent cold reads share one drain, but a\n\t * read after the cache was dropped (a `forceSync`) never joins one started before, which could miss what it asks\n\t * to see.\n\t */\n\tprivate readonly _workspaceProjectsReads = new Map<\n\t\tstring,\n\t\t{ generation: number; read: Promise<ProviderHierarchyResult<ProviderLinearProject>> }\n\t>();\n\tprivate getWorkspaceProjects(\n\t\tsession: ProviderAuthenticationSession,\n\t\tfresh: boolean,\n\t): Promise<ProviderHierarchyResult<ProviderLinearProject>> {\n\t\tconst { accessToken } = session;\n\t\tconst generation = this._workspaceProjects.generation;\n\t\tif (!fresh) {\n\t\t\tconst cached = this._workspaceProjects.get(accessToken);\n\t\t\tif (cached != null) return Promise.resolve({ values: cached });\n\n\t\t\tconst inFlight = this._workspaceProjectsReads.get(accessToken);\n\t\t\tif (inFlight?.generation === generation) return inFlight.read;\n\t\t}\n\n\t\tconst entry = { generation: generation, read: this.readWorkspaceProjects(session) };\n\t\tentry.read = entry.read.then(result => {\n\t\t\tif (\n\t\t\t\tthis._workspaceProjectsReads.get(accessToken) === entry &&\n\t\t\t\tresult.truncated !== true &&\n\t\t\t\t(result.metadata == null || result.metadata.completeness === 'complete')\n\t\t\t) {\n\t\t\t\tthis._workspaceProjects.set(accessToken, result.values, { generation: generation });\n\t\t\t}\n\t\t\treturn result;\n\t\t});\n\t\tthis._workspaceProjectsReads.set(accessToken, entry);\n\t\tvoid entry.read\n\t\t\t.finally(() => {\n\t\t\t\tif (this._workspaceProjectsReads.get(accessToken) === entry) {\n\t\t\t\t\tthis._workspaceProjectsReads.delete(accessToken);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(() => {});\n\t\treturn entry.read;\n\t}\n\n\t/**\n\t * Drains the Projects cursor up to {@link maxWorkspaceProjectPages}. A first-page failure rejects, so the read\n\t * fails as the team read does rather than reading as a workspace with no Projects; a later one keeps the prefix\n\t * and records the failure, as do a stalled cursor and the backstop with pages left.\n\t */\n\tprivate async readWorkspaceProjects(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ProviderHierarchyResult<ProviderLinearProject>> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\tlet pagesRead = 0;\n\t\tlet firstPageError: Error | undefined;\n\t\tconst read = await collectProviderPagedResult(\n\t\t\tasync cursor => {\n\t\t\t\ttry {\n\t\t\t\t\tconst page = await api.getLinearProjects(tokenWithInfo, { cursor: cursor });\n\t\t\t\t\tpagesRead++;\n\t\t\t\t\treturn page;\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tif (pagesRead === 0) {\n\t\t\t\t\t\tfirstPageError = toError(ex);\n\t\t\t\t\t}\n\t\t\t\t\tthrow ex;\n\t\t\t\t}\n\t\t\t},\n\t\t\tmaxWorkspaceProjectPages,\n\t\t\t{ providerId: this.id },\n\t\t);\n\t\t// The drain records any failure against its scope and keeps the prefix; with no prefix, there is no list.\n\t\tif (firstPageError != null) throw firstPageError;\n\n\t\treturn read;\n\t}\n\n\tprotected override async getProviderResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\tconst organization = await this.getOrganization(session);\n\t\treturn organization != null ? [organization] : undefined;\n\t}\n\tprotected override async getProviderUncachedResourcesForUser(\n\t\tsession: ProviderAuthenticationSession,\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\t// Joins none in flight, unlike `getOrganization`: one may have started before what the caller needs to see.\n\t\tconst organization = await this.fetchOrganization(session);\n\t\treturn organization != null ? [organization] : undefined;\n\t}\n\tprotected override getProviderProjectsForResources(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ResourceDescriptor[] | undefined> {\n\t\treturn this.getTeams(session);\n\t}\n\tprotected override async getProviderProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ProviderApiCollectionResult<ResourceDescriptor>> {\n\t\treturn (await this.getTeamsWithMetadata(session)) ?? { values: [], metadata: { completeness: 'unknown' } };\n\t}\n\tprotected override async getProviderUncachedProjectsForResourcesWithMetadata(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resources: ResourceDescriptor[],\n\t): Promise<ProviderApiCollectionResult<ResourceDescriptor>> {\n\t\treturn (await this.readTeams(session)) ?? { values: [], metadata: { completeness: 'unknown' } };\n\t}\n\n\tprotected override clearDiscoveryCaches(): void {\n\t\tthis._organizations = undefined;\n\t\tthis._teams.clear();\n\t\tthis._workspaceProjects.clear();\n\t\tthis._workspaceProjectsReads.clear();\n\t\tthis._autolinks.clear();\n\t\tthis._recentAccounts.clear();\n\t}\n\treadonly authProvider: IntegrationAuthenticationProviderDescriptor = authProvider;\n\n\t/**\n\t * Always asks Linear: the host's current-account cache decides when this runs. The viewer query is account-wide,\n\t * so the answer is lent briefly to the per-resource 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);\n\t}\n\n\tprotected override async getProviderAccountForResource(\n\t\tsession: ProviderAuthenticationSession,\n\t\t_resource: ResourceDescriptor,\n\t): Promise<Account | undefined> {\n\t\treturn this._recentAccounts.getOrCreate(session.accessToken, () => this.fetchAccount(session), {\n\t\t\tevictWhen: account => account == null,\n\t\t});\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(session: ProviderAuthenticationSession): Promise<Account | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\t// Linear's viewer isn't a ProviderAccount (no username/avatar), so build the Account manually\n\t\t// (Trello-style) from the fields the viewer query returns.\n\t\tconst user = await api.getLinearCurrentUser(toTokenWithInfo(this.id, session));\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 ?? user.displayName ?? undefined,\n\t\t\tusername: user.displayName ?? undefined,\n\t\t\temail: user.email ?? undefined,\n\t\t\tavatarUrl: undefined,\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\tif (!isIssueResourceDescriptor(project)) return undefined;\n\n\t\t// Scope to \"my issues\" by the viewer's stable id, not the passed display name: Linear's `name` (full name)\n\t\t// and `displayName` (nickname) are distinct fields, and assignees are normalized with `.name` = the full\n\t\t// name while the caller's `user` is the displayName — so a name string can miss. The assignee `.id` is the\n\t\t// Linear user id, which is unambiguous.\n\t\tlet viewerId: string | undefined;\n\t\tif (options?.user != null) {\n\t\t\tviewerId = await this.getViewerId(session, options);\n\t\t\t// If the viewer can't be resolved we can't scope to \"my issues\" — returning the unfiltered team\n\t\t\t// issues would leak everyone else's, and returning [] is indistinguishable from \"no issues assigned\n\t\t\t// to me\". Throw so the facade (getIssuesForProjectResult → runCaptured) surfaces a warning +\n\t\t\t// fetchFailed the caller can act on, instead of a silent empty.\n\t\t\tif (viewerId == null) {\n\t\t\t\tthrow new IntegrationReadUnavailableError(\n\t\t\t\t\tmetadata.name,\n\t\t\t\t\t'could not resolve the current user to scope issues to',\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// `getProviderProjectsForResources` returns Linear teams, so `project.id` is a team id here.\n\t\t//\n\t\t// The state and the viewer are narrowed server-side, not after the drain: a team's done work and other\n\t\t// people's issues would otherwise count against the backstop, so a busy team runs out of pages before the\n\t\t// viewer's open issues and hands back a page that looks complete.\n\t\tconst drain = await this.drainIssues(\n\t\t\tsession,\n\t\t\t{\n\t\t\t\tteams: [project.id],\n\t\t\t\t...(options?.linearProjects?.length ? { projects: [...options.linearProjects] } : {}),\n\t\t\t\t...(viewerId != null ? viewerScope(viewerId, options?.filters) : {}),\n\t\t\t\tstates: toProviderIssueStates(options?.state),\n\t\t\t},\n\t\t\toptions?.sort,\n\t\t\t{ providerId: this.id, projectId: project.id },\n\t\t);\n\n\t\treturn drain.truncated\n\t\t\t? { values: drain.issues, truncated: true, recovery: 'none', metadata: drain.metadata }\n\t\t\t: { values: drain.issues, truncated: false, metadata: drain.metadata };\n\t}\n\n\t/**\n\t * Searches every team of a user-scoped read together: one `getIssues` names all of them and the viewer's scope\n\t * (see {@link viewerScope}), so a team with none of the user's issues costs no request of its own. Linear's filter\n\t * is a GraphQL body, not a query string, so there is no key bound to chunk at.\n\t *\n\t * A read narrowed to Linear Projects is searched together even across every assignee: the Projects already bound\n\t * it to a few teams' worth of issues, so a team holding none of them costs no request either. An unnarrowed read\n\t * of every assignee is still read team by team, since a search over all of them would just spend one team's page\n\t * budget before falling back.\n\t */\n\tprotected override getProjectIssuesSearches(\n\t\trequests: readonly ProjectIssuesRequest<ResourceDescriptor>[],\n\t): number[][] | undefined {\n\t\treturn groupProjectIssuesSearches(requests, ({ project, options }) =>\n\t\t\t(options.user != null || options.linearProjects?.length) && isIssueResourceDescriptor(project)\n\t\t\t\t? [\n\t\t\t\t\t\toptions.user ?? '',\n\t\t\t\t\t\toptions.userId ?? '',\n\t\t\t\t\t\toptions.sort ?? '',\n\t\t\t\t\t\toptions.state ?? '',\n\t\t\t\t\t\toptions.filters?.join(',') ?? '',\n\t\t\t\t\t\t...(options.linearProjects ?? []),\n\t\t\t\t\t].join('\\0')\n\t\t\t\t: undefined,\n\t\t);\n\t}\n\n\tprotected override async searchProviderProjectIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\trequests: readonly ProjectIssuesRequest<ResourceDescriptor>[],\n\t): Promise<IssueShape[][] | undefined> {\n\t\t// Every request of a search shares every option read here (see the key in `getProjectIssuesSearches`).\n\t\tconst { options } = requests[0];\n\t\tlet viewerId: string | undefined;\n\t\tif (options.user != null) {\n\t\t\t// Unresolvable here means each team's own read throws the same warning, so let them.\n\t\t\tviewerId = await this.getViewerId(session, options);\n\t\t\tif (viewerId == null) return undefined;\n\t\t} else if (!options.linearProjects?.length) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst teams = requests.map(request => request.project).filter(isIssueResourceDescriptor);\n\t\tif (teams.length !== requests.length) return undefined;\n\n\t\tconst drain = await this.drainIssues(\n\t\t\tsession,\n\t\t\t{\n\t\t\t\tteams: teams.map(team => team.id),\n\t\t\t\t...(options.linearProjects?.length ? { projects: [...options.linearProjects] } : {}),\n\t\t\t\t...(viewerId != null ? viewerScope(viewerId, options.filters) : {}),\n\t\t\t\tstates: toProviderIssueStates(options.state),\n\t\t\t},\n\t\t\toptions.sort,\n\t\t\t// Never published: an incomplete search is read again per team, which records its own failures.\n\t\t\t{ providerId: this.id },\n\t\t);\n\t\t// Deliberately discarded rather than served: an incomplete search says nothing about which of its teams it\n\t\t// covered, so only the per-team reads can report each one's completeness. The waste is bounded by one team's\n\t\t// page budget, and paid only by a search that needed more than that or failed partway.\n\t\tif (drain.truncated || (drain.metadata != null && drain.metadata.completeness !== 'complete')) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// A normalized Linear issue carries its team only inside `project`, which is absent for an issue in no\n\t\t// Linear project, so split by the identifier instead: Linear numbers every issue `<team key>-<n>`, and\n\t\t// renumbers it under its new team's key when it moves.\n\t\treturn splitProjectIssuesSearch(\n\t\t\tteams.map(team => team.key),\n\t\t\tdrain.issues,\n\t\t\tissue => {\n\t\t\t\tconst separator = issue.id.lastIndexOf('-');\n\t\t\t\treturn separator > 0 ? issue.id.slice(0, separator) : undefined;\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate readonly _viewerIds = new PromiseCache<string, string | undefined>({ capacity: 10 });\n\t/**\n\t * The viewer's Linear user id: the caller's resolved `userId` when it has one, else the viewer query, shared per\n\t * token. A tracker read hands each team the id of the team's own resource, which Linear's team descriptors don't\n\t * name, so without sharing every team read (and a search's fallback) would ask for the viewer again.\n\t */\n\tprivate getViewerId(\n\t\tsession: ProviderAuthenticationSession,\n\t\toptions: IssuesForProjectOptions,\n\t): Promise<string | undefined> {\n\t\tif (options.userId) return Promise.resolve(options.userId);\n\n\t\treturn this._viewerIds.getOrCreate(\n\t\t\tsession.accessToken,\n\t\t\tasync () => {\n\t\t\t\tconst api = await this.getProvidersApi();\n\t\t\t\treturn (await api.getLinearCurrentUser(toTokenWithInfo(this.id, session)))?.id;\n\t\t\t},\n\t\t\t// Only a resolved id is worth keeping; an unresolved viewer is asked for again next time, as is a failure.\n\t\t\t{ evictWhen: id => id == null },\n\t\t);\n\t}\n\n\t/**\n\t * Follows `getIssues`' cursor up to {@link maxPagesPerRequest}. `truncated` is set when that backstop stopped\n\t * the drain with more pages still available, when the provider stalled its cursor or flagged its paging as\n\t * truncated, or when a page after the first failed, which is recorded at `failureScope` while the prefix is\n\t * kept. A failure before any issue was kept throws, so the caller sees a hard error rather than an empty success.\n\t */\n\tprivate async drainIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tfilter: { teams: string[]; projects?: string[]; states?: GitIssueState[] } & Partial<LinearViewerScope>,\n\t\tsort: IssuesForProjectOptions['sort'],\n\t\tfailureScope: CollectionScopeFailure['scope'],\n\t): Promise<{ issues: IssueShape[]; truncated: boolean; metadata?: CollectionMetadata }> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst assignees = filter.assignees?.length ? filter.assignees : undefined;\n\t\tconst syncedCreators = filter.githubSyncedCreators?.length ? filter.githubSyncedCreators : undefined;\n\t\tlet cursor: string | undefined;\n\t\tlet hasMore: boolean;\n\t\tlet requestCount = 0;\n\t\tlet truncated = false;\n\t\tlet collectionMetadata: CollectionMetadata | undefined;\n\t\tconst issues: IssueShape[] = [];\n\t\tdo {\n\t\t\tlet result: Awaited<ReturnType<typeof api.getLinearIssues>>;\n\t\t\ttry {\n\t\t\t\tresult = await api.getLinearIssues(toTokenWithInfo(this.id, session), filter, {\n\t\t\t\t\tcursor: cursor,\n\t\t\t\t\tsort: sort,\n\t\t\t\t});\n\t\t\t} catch (ex) {\n\t\t\t\t// Keyed on what was kept, not on the page: provider-apis can empty a page it filters after the query\n\t\t\t\t// (synced-creator reads) and still report more, and a drain that kept nothing has nothing to serve, so\n\t\t\t\t// its caller retries it rather than recording an empty prefix as read.\n\t\t\t\tif (issues.length === 0) throw ex;\n\n\t\t\t\ttruncated = true;\n\t\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, {\n\t\t\t\t\tcompleteness: 'partial',\n\t\t\t\t\tfailures: [toCollectionScopeFailure(failureScope, ex)],\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trequestCount += 1;\n\t\t\thasMore = result.paging?.more ?? false;\n\t\t\tconst nextCursor = result.paging?.cursor;\n\t\t\ttruncated ||= result.paging?.truncated === true;\n\t\t\tcollectionMetadata = mergeCollectionMetadata(collectionMetadata, result.metadata);\n\t\t\tfor (const issue of result.values) {\n\t\t\t\tconst shape = toIssueShape(issue, this, { projection: 'project' });\n\t\t\t\t// The read already scopes to the assignees and to the synced issues the creators filed, leaving out the\n\t\t\t\t// issues with no GitHub sync link (`[]`) and keeping one whose link list Linear failed to resolve\n\t\t\t\t// (absent). Checking again keeps another user's issue, or an unsynced one only its GitHub attachment\n\t\t\t\t// matched, out of \"my issues\" should the provider ever return one.\n\t\t\t\tif (\n\t\t\t\t\tshape != null &&\n\t\t\t\t\t(assignees == null ||\n\t\t\t\t\t\tshape.assignees?.some(a => assignees.includes(a.id)) ||\n\t\t\t\t\t\t(syncedCreators != null &&\n\t\t\t\t\t\t\tshape.author != null &&\n\t\t\t\t\t\t\tsyncedCreators.includes(shape.author.id) &&\n\t\t\t\t\t\t\tshape.syncedWith?.length !== 0))\n\t\t\t\t) {\n\t\t\t\t\tissues.push(shape);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The provider claims more but returns no advancing cursor: we can't continue without re-reading the\n\t\t\t// same page, so the drain is incomplete — flag it rather than silently stopping.\n\t\t\tif (hasMore && (nextCursor == null || nextCursor === cursor)) {\n\t\t\t\ttruncated = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcursor = nextCursor;\n\t\t\tif (hasMore && requestCount >= maxPagesPerRequest) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t} while (requestCount < maxPagesPerRequest && hasMore);\n\n\t\treturn { issues: issues, truncated: truncated, metadata: collectionMetadata };\n\t}\n\n\toverride get id(): IssuesCloudHostIntegrationId.Linear {\n\t\treturn IssuesCloudHostIntegrationId.Linear;\n\t}\n\tprotected override get key(): 'linear' {\n\t\treturn 'linear';\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\tprotected override async searchProviderMyIssues(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueShape[] | undefined> {\n\t\treturn (await this.searchProviderMyIssuesWithTruncation(session, resources, cancellation))?.values;\n\t}\n\n\t/**\n\t * Account-wide \"my issues\" for Linear, drained to exhaustion and reporting whether it got there.\n\t *\n\t * The SDK read is one server-ordered query over an `or` filter across the four relationships (assigned,\n\t * created, named in an issue's content, named in a comment), so each page is 100 issues the caller has not\n\t * seen and the pages are ordered as one sequence. Draining it is therefore just following the cursor.\n\t *\n\t * Overridden rather than left to the base default because that default hardcodes `truncated: false`, and a\n\t * bounded drain that stops early is exactly the case a caller must be able to see. Three things end the\n\t * drain, and only one of them is completion:\n\t *\n\t * - the provider says there is no next page — complete;\n\t * - the backstop is reached — `truncated`, because issues beyond it exist and were not read;\n\t * - the provider claims a next page but hands back a cursor that does not advance — also `truncated`, since\n\t * the read cannot continue and the remainder is unreachable rather than absent.\n\t *\n\t * Cancellation is deliberately NOT truncation: the caller asked for the read to stop, so the partial list is\n\t * what it asked for, and flagging it would surface an incompleteness warning for a user action.\n\t */\n\tprotected override async searchProviderMyIssuesWithTruncation(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresources?: ResourceDescriptor[],\n\t\tcancellation?: AbortSignal,\n\t\toptions?: SearchMyIssuesOptions,\n\t): Promise<AccountWideIssuesResult | undefined> {\n\t\tif (resources != null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst api = await this.getProvidersApi();\n\t\tconst states = toProviderIssueStates(options?.state);\n\t\tlet cursor = undefined;\n\t\t// Starts false so an immediate cancellation, which leaves the loop before the first response, reads as\n\t\t// \"no more pages known\" rather than as an unfinished drain.\n\t\tlet hasMore = false;\n\t\tlet requestCount = 0;\n\t\tlet truncated = false;\n\t\tconst issues = [];\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tif (cancellation?.aborted) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst result = await api.getIssuesForCurrentUser(toTokenWithInfo(this.id, session), {\n\t\t\t\t\tcursor: cursor,\n\t\t\t\t\tstates: states,\n\t\t\t\t\tsort: options?.sort,\n\t\t\t\t});\n\t\t\t\trequestCount += 1;\n\t\t\t\thasMore = result.paging?.more ?? false;\n\t\t\t\tconst nextCursor = result.paging?.cursor;\n\n\t\t\t\t// Keep this page before deciding whether to continue: the request is already paid for, so\n\t\t\t\t// dropping its rows would lose real results to save nothing.\n\t\t\t\tconst formattedIssues = result.values\n\t\t\t\t\t.map(issue => toIssueShape(issue, this, { projection: 'account' }))\n\t\t\t\t\t.filter((result): result is IssueShape => result != null);\n\t\t\t\tif (formattedIssues.length > 0) {\n\t\t\t\t\tissues.push(...formattedIssues);\n\t\t\t\t}\n\n\t\t\t\t// The provider claims more but hands back no advancing cursor: continuing would re-read the page\n\t\t\t\t// just returned. `ProvidersApi.getPagedResult` already normalizes this into `more: false`, so\n\t\t\t\t// this is a backstop for if that ever stops holding rather than a case seen today -- but the rest\n\t\t\t\t// of the account is unreachable either way, which is what `truncated` says.\n\t\t\t\tif (hasMore && (nextCursor == null || nextCursor === cursor)) {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcursor = nextCursor;\n\t\t\t} while (requestCount < maxAccountWidePagesPerRequest && hasMore);\n\n\t\t\t// Ran out of budget with pages still to come.\n\t\t\tif (hasMore && requestCount >= maxAccountWidePagesPerRequest) {\n\t\t\t\ttruncated = true;\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (issues.length === 0) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\t// Kept what was already fetched, so the list is real but short of the account.\n\t\t\ttruncated = true;\n\t\t\tLogger.error(ex, 'searchProviderMyIssues');\n\t\t}\n\t\treturn { values: issues, truncated: truncated };\n\t}\n\tprotected override async getProviderLinkedIssueOrPullRequest(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\t{ key }: { id: string; key: string },\n\t\t_type: undefined | IssueOrPullRequestType,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst issue = await this.getRawProviderIssue(session, resource, key);\n\t\tconst autolinkableIssue: ProviderIssue | undefined = issue && {\n\t\t\t...issue,\n\t\t\turl: this.getIssueAutolinkLikeUrl(issue),\n\t\t};\n\t\treturn autolinkableIssue && toIssueShape(autolinkableIssue, this, { projection: 'point' });\n\t}\n\tprotected override async getProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<Issue | undefined> {\n\t\tconst result = await this.getRawProviderIssue(session, resource, id);\n\t\treturn result && fromProviderIssue(result, this, { projection: 'point' });\n\t}\n\n\tprotected override async getProviderIssueByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresourceId: string,\n\t\tid: string,\n\t\t_resourceUrl: string | undefined,\n\t): Promise<Issue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tconst result = await api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resourceId,\n\t\t\tnumber: id,\n\t\t});\n\t\treturn result && fromProviderIssue(result, this, { projection: 'batch' });\n\t}\n\n\t/**\n\t * The cheap check behind the batch issue read's etags: one `issues` query per team and\n\t * {@link linearIssuesEtagMaxNumbers} distinct numbers, where {@link getProviderIssueByResourceId} sends one request\n\t * per issue. A request that throws rejects only its own targets' slots, with its error classified as the full\n\t * read's would be.\n\t *\n\t * Never proves an absence. The query filters by the team's CURRENT key and asks for archived issues too, but the\n\t * full read's `issue(id:)` may also resolve an identifier the issue no longer carries (its team was renamed, or it\n\t * moved to another team), so an issue the query leaves out may still exist. Its target's slot is rejected, as is a\n\t * target whose identifier isn't a team key and a number, so the full read decides. When no target was answered and\n\t * nothing failed, the check declines instead, so a batch of only such targets costs a full read, not a failure.\n\t */\n\tprotected override async getProviderIssuesEtagFieldsByResourceId(\n\t\tsession: ProviderAuthenticationSession,\n\t\ttargets: readonly { resourceId: string; identifier: string; resourceUrl?: string }[],\n\t): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined> {\n\t\tconst slots = new Array<PromiseSettledResult<IssueEtagFields | undefined>>(targets.length);\n\t\tlet answered = false;\n\t\tlet failed = false;\n\n\t\t// Each workspace and team's distinct numbers, with the targets asking for each.\n\t\tconst byTeam = new Map<string, { teamKey: string; numbers: Map<number, number[]> }>();\n\t\tfor (const [index, target] of targets.entries()) {\n\t\t\tconst match = linearIdentifier.exec(target.identifier);\n\t\t\tconst number = match != null ? Number(match[2]) : Number.NaN;\n\t\t\t// Only a number written as Linear writes it, so `ENG-012` never answers for `ENG-12`.\n\t\t\tif (match == null || !Number.isSafeInteger(number) || String(number) !== match[2]) {\n\t\t\t\tslots[index] = {\n\t\t\t\t\tstatus: 'rejected',\n\t\t\t\t\treason: new Error(`Not a Linear issue identifier: ${target.identifier}`),\n\t\t\t\t};\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst teamKey = match[1].toUpperCase();\n\t\t\tconst groupKey = `${target.resourceId}\\n${teamKey}`;\n\t\t\tlet group = byTeam.get(groupKey);\n\t\t\tif (group == null) {\n\t\t\t\tgroup = { teamKey: teamKey, numbers: new Map() };\n\t\t\t\tbyTeam.set(groupKey, group);\n\t\t\t}\n\n\t\t\tlet indices = group.numbers.get(number);\n\t\t\tif (indices == null) {\n\t\t\t\tindices = [];\n\t\t\t\tgroup.numbers.set(number, indices);\n\t\t\t}\n\t\t\tindices.push(index);\n\t\t}\n\n\t\tconst requests = [...byTeam.values()].flatMap(({ teamKey, numbers }) =>\n\t\t\tchunk([...numbers], linearIssuesEtagMaxNumbers).map(entries => ({ teamKey: teamKey, entries: entries })),\n\t\t);\n\t\tif (requests.length) {\n\t\t\tconst api = await this.getProvidersApi();\n\t\t\tconst tokenWithInfo = toTokenWithInfo(this.id, session);\n\t\t\tconst answers = await mapSettledBounded(requests, providerFanOutConcurrency, r =>\n\t\t\t\tapi.getLinearIssuesEtagFields(\n\t\t\t\t\ttokenWithInfo,\n\t\t\t\t\tr.teamKey,\n\t\t\t\t\tr.entries.map(([number]) => number),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tfor (const [i, answer] of answers.entries()) {\n\t\t\t\tconst { teamKey, entries } = requests[i];\n\t\t\t\tif (answer.status === 'rejected') {\n\t\t\t\t\tfailed = true;\n\t\t\t\t\tfor (const [, indices] of entries) {\n\t\t\t\t\t\tfor (const index of indices) {\n\t\t\t\t\t\t\tslots[index] = answer;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// By number, never by position: Linear returns the issues in its own order, and only the ones it found.\n\t\t\t\tconst byNumber = new Map<number, LinearIssueEtagNode>();\n\t\t\t\tfor (const issue of answer.value) {\n\t\t\t\t\tbyNumber.set(issue.number, issue);\n\t\t\t\t}\n\n\t\t\t\tfor (const [number, indices] of entries) {\n\t\t\t\t\tconst issue = byNumber.get(number);\n\t\t\t\t\tlet slot: PromiseSettledResult<IssueEtagFields | undefined>;\n\t\t\t\t\tif (issue == null) {\n\t\t\t\t\t\tslot = { status: 'rejected', reason: new Error(`Linear did not return ${teamKey}-${number}`) };\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tslot = { status: 'fulfilled', value: toLinearIssueEtagFields(issue) };\n\t\t\t\t\t\t\tanswered = true;\n\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\tslot = { status: 'rejected', reason: ex };\n\t\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const index of indices) {\n\t\t\t\t\t\tslots[index] = slot;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn answered || failed ? slots : undefined;\n\t}\n\n\tprivate async getRawProviderIssue(\n\t\tsession: ProviderAuthenticationSession,\n\t\tresource: ResourceDescriptor,\n\t\tid: string,\n\t): Promise<ProviderIssue | undefined> {\n\t\tconst api = await this.getProvidersApi();\n\t\tif (!isIssueResourceDescriptor(resource)) {\n\t\t\tLogger.error(undefined, 'getProviderIssue: resource is not an IssueResourceDescriptor');\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn api.getIssue(toTokenWithInfo(this.id, session), {\n\t\t\tresourceId: resource.id,\n\t\t\tnumber: id,\n\t\t});\n\t}\n\tprivate getIssueAutolinkLikeUrl(issue: ProviderIssue): string | null {\n\t\tconst url = issue.url;\n\t\tif (url == null) return null;\n\n\t\tconst lastSegment = url.split('/').pop();\n\t\tif (!lastSegment || issue.number === lastSegment) {\n\t\t\treturn url;\n\t\t}\n\t\treturn url.substring(0, url.length - lastSegment.length - 1);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ import { ProviderOrganization, ProviderRepositoryShape } from "../results.js";
|
|
|
10
10
|
import { IssueFilter } from "../providerFilters.js";
|
|
11
11
|
import { ProviderPullRequestReviews } from "./pullRequestReviews.js";
|
|
12
12
|
import { Integration, IntegrationType } from "../models/integration.js";
|
|
13
|
-
import { Account, ActionablePullRequest, AnyEntityIdentifierInput, AzureDevOps, AzureOrganization, AzureProject, AzureSetPullRequestInput, Bitbucket, BitbucketServer, BitbucketWorkspaceStub, CollectionMetadata, CursorPageInput, EnterpriseOptions, GetRepoInput, GitBuildStatus, GitBuildStatusState as GitBuildStatusState$1, GitHub, GitHubPullRequestFieldMap, GitIssueState as GitIssueState$1, GitLab, GitLabGroup, GitMergeStrategy, GitPullRequest, GitPullRequestMergeableState as GitPullRequestMergeableState$1, GitPullRequestReviewState as GitPullRequestReviewState$1, GitPullRequestState as GitPullRequestState$1, GitRepository, Issue, Jira, JiraProject, JiraResource, JiraServer, JiraServerProject, Linear, LinearOrganization, LinearTeam, NumberedPageInput, Organization, PullRequestWithUniqueID, RequestFunction, RequestOptions, Response, SetPullRequestInput, Trello } from "@gitkraken/provider-apis";
|
|
13
|
+
import { Account, ActionablePullRequest, AnyEntityIdentifierInput, AzureDevOps, AzureOrganization, AzureProject, AzureSetPullRequestInput, Bitbucket, BitbucketServer, BitbucketWorkspaceStub, CollectionMetadata, CursorPageInput, EnterpriseOptions, GetRepoInput, GitBuildStatus, GitBuildStatusState as GitBuildStatusState$1, GitHub, GitHubPullRequestFieldMap, GitIssueState as GitIssueState$1, GitLab, GitLabGroup, GitMergeStrategy, GitPullRequest, GitPullRequestMergeableState as GitPullRequestMergeableState$1, GitPullRequestReviewState as GitPullRequestReviewState$1, GitPullRequestState as GitPullRequestState$1, GitRepository, Issue, Jira, JiraProject, JiraResource, JiraServer, JiraServerProject, Linear, LinearOrganization, LinearProject, LinearTeam, NumberedPageInput, Organization, PullRequestWithUniqueID, RequestFunction, RequestOptions, Response, SetPullRequestInput, Trello } from "@gitkraken/provider-apis";
|
|
14
14
|
//#region ../plus/integrations/src/providers/models.d.ts
|
|
15
15
|
type GitBuildStatusState = GitBuildStatusState$1;
|
|
16
16
|
type GitIssueState = GitIssueState$1;
|
|
@@ -93,6 +93,7 @@ export type ProviderJiraResource = JiraResource;
|
|
|
93
93
|
export type ProviderJiraServerProject = JiraServerProject;
|
|
94
94
|
export type ProviderLinearTeam = LinearTeam;
|
|
95
95
|
export type ProviderLinearOrganization = LinearOrganization;
|
|
96
|
+
export type ProviderLinearProject = LinearProject;
|
|
96
97
|
export type ProviderAzureProject = AzureProject;
|
|
97
98
|
export type ProviderAzureResource = AzureOrganization;
|
|
98
99
|
export type ProviderBitbucketResource = BitbucketWorkspaceStub;
|
|
@@ -444,6 +445,7 @@ export type GetLinearTeamsForCurrentUserFn = (options?: EnterpriseOptions) => Pr
|
|
|
444
445
|
data: LinearTeam[];
|
|
445
446
|
}>;
|
|
446
447
|
export type GetLinearIssuesFn = Linear['getIssues'];
|
|
448
|
+
export type GetLinearProjectsFn = Linear['getProjects'];
|
|
447
449
|
/**
|
|
448
450
|
* Linear's current-user (viewer) query. Its raw `@linear/sdk` User isn't a `ProviderAccount` (no
|
|
449
451
|
* username/avatar/url), so it's typed with the minimal fields the viewer query actually returns rather than
|
|
@@ -571,6 +573,7 @@ export interface ProviderInfo extends ProviderMetadata {
|
|
|
571
573
|
getLinearOrganizationFn?: GetLinearOrganizationFn;
|
|
572
574
|
getLinearTeamsForCurrentUserFn?: GetLinearTeamsForCurrentUserFn;
|
|
573
575
|
getLinearIssuesFn?: GetLinearIssuesFn;
|
|
576
|
+
getLinearProjectsFn?: GetLinearProjectsFn;
|
|
574
577
|
getLinearCurrentUserFn?: GetLinearCurrentUserFn;
|
|
575
578
|
getAzureResourcesForUserFn?: GetAzureResourcesForUserFn;
|
|
576
579
|
getBitbucketResourcesForCurrentUserFn?: GetBitbucketResourcesForCurrentUserFn;
|