@gitkraken/core-gitlens 0.23.0 → 0.25.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.
Files changed (152) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/README.md +23 -15
  3. package/dist/git/errors.d.ts +6 -5
  4. package/dist/git/errors.js +7 -6
  5. package/dist/git/errors.js.map +1 -1
  6. package/dist/git/features.d.ts +1 -1
  7. package/dist/git/features.js +1 -1
  8. package/dist/git/features.js.map +1 -1
  9. package/dist/git/models/remoteProvider.d.ts +4 -1
  10. package/dist/git/models/remoteProvider.js.map +1 -1
  11. package/dist/git/providers/provider.d.ts +4 -2
  12. package/dist/git/utils/branch.utils.d.ts +4 -1
  13. package/dist/git/utils/branch.utils.js +5 -2
  14. package/dist/git/utils/branch.utils.js.map +1 -1
  15. package/dist/git/utils/mergeConflicts.utils.js +2 -2
  16. package/dist/git/utils/mergeConflicts.utils.js.map +1 -1
  17. package/dist/git-cli/cliGitProvider.d.ts +14 -3
  18. package/dist/git-cli/cliGitProvider.js +71 -34
  19. package/dist/git-cli/cliGitProvider.js.map +1 -1
  20. package/dist/git-cli/exec/git.d.ts +7 -1
  21. package/dist/git-cli/exec/git.js +19 -4
  22. package/dist/git-cli/exec/git.js.map +1 -1
  23. package/dist/git-cli/providers/branchRemote.js +2 -5
  24. package/dist/git-cli/providers/branchRemote.js.map +1 -1
  25. package/dist/git-cli/providers/branches.js +4 -4
  26. package/dist/git-cli/providers/branches.js.map +1 -1
  27. package/dist/git-cli/providers/config.js +6 -3
  28. package/dist/git-cli/providers/config.js.map +1 -1
  29. package/dist/git-cli/providers/pausedOperations.js +2 -2
  30. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  31. package/dist/git-cli/providers/staging.js +1 -1
  32. package/dist/ipc/discovery.js +1 -1
  33. package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
  34. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
  35. package/dist/plus/ai/errors.d.ts +4 -1
  36. package/dist/plus/ai/errors.js +4 -0
  37. package/dist/plus/ai/errors.js.map +1 -1
  38. package/dist/plus/ai/providers/mistralProvider.js +1 -1
  39. package/dist/plus/ai/providers/mistralProvider.js.map +1 -1
  40. package/dist/plus/ai/providers/openAICompatibleProviderBase.js +2 -1
  41. package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
  42. package/dist/plus/git-github/api/github.js +12 -13
  43. package/dist/plus/git-github/api/github.js.map +1 -1
  44. package/dist/plus/git-github/api/token.d.ts +2 -0
  45. package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts +1 -1
  46. package/dist/plus/integrations/authentication/cloudIntegrationService.js +6 -1
  47. package/dist/plus/integrations/authentication/cloudIntegrationService.js.map +1 -1
  48. package/dist/plus/integrations/authentication/configuredIntegrationService.js +2 -2
  49. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +18 -5
  50. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js.map +1 -1
  51. package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts +3 -1
  52. package/dist/plus/integrations/authentication/integrationAuthenticationService.js +4 -0
  53. package/dist/plus/integrations/authentication/integrationAuthenticationService.js.map +1 -1
  54. package/dist/plus/integrations/authentication/models.d.ts +2 -0
  55. package/dist/plus/integrations/authentication/models.js +2 -1
  56. package/dist/plus/integrations/authentication/models.js.map +1 -1
  57. package/dist/plus/integrations/authentication/observedAuthenticationProvider.js +1 -1
  58. package/dist/plus/integrations/authentication/rejectedTokenTracker.d.ts +5 -4
  59. package/dist/plus/integrations/authentication/rejectedTokenTracker.js +5 -4
  60. package/dist/plus/integrations/authentication/rejectedTokenTracker.js.map +1 -1
  61. package/dist/plus/integrations/index.d.ts +2 -2
  62. package/dist/plus/integrations/index.js +1 -1
  63. package/dist/plus/integrations/index.js.map +1 -1
  64. package/dist/plus/integrations/integrationService.d.ts +6 -0
  65. package/dist/plus/integrations/integrationService.js +3 -3
  66. package/dist/plus/integrations/integrationService.js.map +1 -1
  67. package/dist/plus/integrations/lite.js +1 -1
  68. package/dist/plus/integrations/manager.d.ts +51 -3
  69. package/dist/plus/integrations/models/gitHostIntegration.d.ts +14 -0
  70. package/dist/plus/integrations/models/gitHostIntegration.js +50 -41
  71. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  72. package/dist/plus/integrations/models/integration.d.ts +27 -5
  73. package/dist/plus/integrations/models/integration.js +57 -15
  74. package/dist/plus/integrations/models/integration.js.map +1 -1
  75. package/dist/plus/integrations/models/issueReads.d.ts +6 -0
  76. package/dist/plus/integrations/models/issuesIntegration.js +11 -11
  77. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  78. package/dist/plus/integrations/providers/azure/azure.js +2 -2
  79. package/dist/plus/integrations/providers/azureDevOps.js +1 -1
  80. package/dist/plus/integrations/providers/azureDevOpsServer.js +1 -1
  81. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +1 -1
  82. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
  83. package/dist/plus/integrations/providers/bitbucket-server.js +1 -1
  84. package/dist/plus/integrations/providers/bitbucket.js +1 -1
  85. package/dist/plus/integrations/providers/github.js +1 -1
  86. package/dist/plus/integrations/providers/gitlab/gitlab.js +2 -2
  87. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
  88. package/dist/plus/integrations/providers/gitlab.js +1 -1
  89. package/dist/plus/integrations/providers/jira-server.js +2 -2
  90. package/dist/plus/integrations/providers/jira.js +1 -1
  91. package/dist/plus/integrations/providers/linear.d.ts +4 -4
  92. package/dist/plus/integrations/providers/linear.js +26 -9
  93. package/dist/plus/integrations/providers/linear.js.map +1 -1
  94. package/dist/plus/integrations/providers/models.js +2 -1
  95. package/dist/plus/integrations/providers/models.js.map +1 -1
  96. package/dist/plus/integrations/providers/providerErrors.js +9 -0
  97. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  98. package/dist/plus/integrations/providers/providersApi.d.ts +2 -0
  99. package/dist/plus/integrations/providers/providersApi.js +1 -1
  100. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  101. package/dist/plus/integrations/providers/trello.js +1 -1
  102. package/dist/plus/integrations/providers/utils.js +1 -1
  103. package/dist/plus/integrations/reads/counts.js +1 -1
  104. package/dist/plus/integrations/reads/cursors.js +1 -1
  105. package/dist/plus/integrations/reads/drains.d.ts +5 -4
  106. package/dist/plus/integrations/reads/drains.js +28 -8
  107. package/dist/plus/integrations/reads/drains.js.map +1 -1
  108. package/dist/plus/integrations/reads/hierarchy.js +1 -1
  109. package/dist/plus/integrations/reads/issueBatch.d.ts +1 -1
  110. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  111. package/dist/plus/integrations/reads/issueTracker.d.ts +4 -1
  112. package/dist/plus/integrations/reads/issueTracker.js +1 -1
  113. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  114. package/dist/plus/integrations/reads/issues.d.ts +2 -0
  115. package/dist/plus/integrations/reads/issues.js +5 -3
  116. package/dist/plus/integrations/reads/issues.js.map +1 -1
  117. package/dist/plus/integrations/reads/paging.d.ts +5 -6
  118. package/dist/plus/integrations/reads/paging.js +5 -6
  119. package/dist/plus/integrations/reads/paging.js.map +1 -1
  120. package/dist/plus/integrations/reads/pullRequests.d.ts +4 -0
  121. package/dist/plus/integrations/reads/pullRequests.js +4 -2
  122. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  123. package/dist/plus/integrations/reads/resolveRepository.js +1 -1
  124. package/dist/plus/integrations/reads/sweeps.js +42 -10
  125. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  126. package/dist/plus/integrations/results.d.ts +23 -3
  127. package/dist/plus/integrations/results.js.map +1 -1
  128. package/dist/plus/notifications/api.d.ts +20 -0
  129. package/dist/plus/notifications/api.js +89 -0
  130. package/dist/plus/notifications/api.js.map +1 -0
  131. package/dist/plus/notifications/context.d.ts +28 -0
  132. package/dist/plus/notifications/context.js +0 -0
  133. package/dist/plus/notifications/errors.d.ts +13 -0
  134. package/dist/plus/notifications/errors.js +22 -0
  135. package/dist/plus/notifications/errors.js.map +1 -0
  136. package/dist/plus/notifications/models.d.ts +94 -0
  137. package/dist/plus/notifications/models.js +0 -0
  138. package/dist/plus/notifications/notificationsService.d.ts +115 -0
  139. package/dist/plus/notifications/notificationsService.js +381 -0
  140. package/dist/plus/notifications/notificationsService.js.map +1 -0
  141. package/dist/plus/notifications/parse.d.ts +12 -0
  142. package/dist/plus/notifications/parse.js +152 -0
  143. package/dist/plus/notifications/parse.js.map +1 -0
  144. package/dist/plus/notifications/presentation.d.ts +28 -0
  145. package/dist/plus/notifications/presentation.js +55 -0
  146. package/dist/plus/notifications/presentation.js.map +1 -0
  147. package/dist/utils/env/node/exec.d.ts +10 -1
  148. package/dist/utils/env/node/exec.js +41 -4
  149. package/dist/utils/env/node/exec.js.map +1 -1
  150. package/docs/integrations.md +25 -3
  151. package/docs/notifications.md +239 -0
  152. package/package.json +22 -2
@@ -1 +1 @@
1
- {"version":3,"file":"gitlab.js","names":["Logger"],"sources":["../../../../../../plus/integrations/src/providers/gitlab/gitlab.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport { reportRequestFailure } from '@gitlens/git/errors.js';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { IssueOrPullRequest } from '@gitlens/git/models/issueOrPullRequest.js';\nimport { PullRequest } from '@gitlens/git/models/pullRequest.js';\nimport type { PullRequestState } from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport type { Disposable } from '@gitlens/utils/disposable.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { equalsIgnoreCase } from '@gitlens/utils/string.js';\nimport { joinUriPath, parseUri } from '@gitlens/utils/uri.js';\nimport type { TokenWithInfo } from '../../authentication/models.js';\nimport type { IntegrationServiceContext } from '../../context.js';\nimport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tisRateLimitResponse,\n\tProviderFetchError,\n\tRequestClientError,\n\tRequestNotFoundError,\n\ttoRateLimitError,\n} from '../../errors.js';\nimport type { IssueEtagInclude, PullRequestEtagInclude } from '../../models/integration.js';\nimport type { ProviderApiConfig } from '../apiConfig.js';\nimport { baseProviderApiConfig } from '../apiConfig.js';\nimport { selectBranchPullRequests } from '../utils/providerPaging.js';\nimport { selectGitLabUserForCommit } from './gitlab.utils.js';\nimport type {\n\tGitLabCommit,\n\tGitLabIssue,\n\tGitLabIssueEtagNode,\n\tGitLabMergeRequest,\n\tGitLabMergeRequestEtagNode,\n\tGitLabMergeRequestFull,\n\tGitLabMergeRequestREST,\n\tGitLabMergeRequestState,\n\tGitLabProjectREST,\n\tGitLabSshKey,\n\tGitLabUser,\n} from './models.js';\nimport {\n\tfromGitLabMergeRequest,\n\tfromGitLabMergeRequestREST,\n\tfromGitLabMergeRequestState,\n\tgetRepoNamespace,\n\tgitLabEtagFieldsMaxIids,\n\ttoGitLabMergeRequestState,\n} from './models.js';\n\n// drop it as soon as we switch to @gitkraken/providers-api\nconst gitlabUserIdPrefix = 'gid://gitlab/User/';\nconst gitlabMergeRequestIdPrefix = 'gid://gitlab/MergeRequest/';\n\nfunction buildGitLabUserId(id: string | number | undefined): string | undefined {\n\treturn typeof id === 'string' && id?.startsWith(gitlabUserIdPrefix)\n\t\t? id.substring(gitlabUserIdPrefix.length)\n\t\t: String(id);\n}\n\nexport class GitLabApi implements Disposable {\n\tprivate readonly _disposable: Disposable | undefined;\n\tprivate _projectIds = new Map<string, Promise<string | undefined>>();\n\n\tconstructor(private readonly config: ProviderApiConfig) {\n\t\tthis._disposable = config.onConfigChanged?.(() => this.resetCaches());\n\t}\n\n\tdispose(): void {\n\t\tthis._disposable?.dispose();\n\t}\n\n\tprivate resetCaches(): void {\n\t\tthis._projectIds.clear();\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, rev) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\trev: rev,\n\t\t}),\n\t})\n\tasync getAccountForCommit(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<Account | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst projectId = await this.getProjectId(provider, token, owner, repo, options?.baseUrl, cancellation);\n\t\tif (!projectId) return undefined;\n\n\t\ttry {\n\t\t\tconst commit = await this.request<GitLabCommit>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`v4/projects/${projectId}/repository/commits/${rev}?stats=false`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t// ...options,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tlet users = await this.findUser(provider, token, commit.author_name, options, cancellation);\n\t\t\tlet user = selectGitLabUserForCommit(users, commit.author_name, commit.author_email);\n\n\t\t\t// If the name search was ambiguous (multiple users share the name), try an email-scoped search to\n\t\t\t// disambiguate before giving up — otherwise we'd show a stranger's avatar (see #2205)\n\t\t\tif (user == null && commit.author_email) {\n\t\t\t\tconst exactNameMatches = users.filter(u => equalsIgnoreCase(u.name, commit.author_name)).length;\n\t\t\t\tif (exactNameMatches > 1) {\n\t\t\t\t\tusers = await this.findUser(provider, token, commit.author_email, options, cancellation);\n\t\t\t\t\tuser = selectGitLabUserForCommit(users, commit.author_name, commit.author_email);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (user == null) return undefined;\n\n\t\t\t// If the avatarUrl is a relative URL, make it absolute using the webUrl (assuming the webUrl is the root URL with the username tacked on)\n\t\t\tif (user.avatarUrl && !/^([a-zA-Z][\\w+.-]+):/.test(user.avatarUrl)) {\n\t\t\t\tuser.avatarUrl = joinUriPath(parseUri(user.webUrl), '..', user.avatarUrl).toString();\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tid: String(user.id),\n\t\t\t\tname: user.name || undefined,\n\t\t\t\temail: commit.author_email || undefined,\n\t\t\t\tavatarUrl: user.avatarUrl || undefined,\n\t\t\t\tusername: user.username || undefined,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, _owner, _repo, email) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\temail: email,\n\t\t}),\n\t})\n\tasync getAccountForEmail(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\t_owner: string,\n\t\t_repo: string,\n\t\temail: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst [user] = await this.findUser(provider, token, email, options);\n\t\t\tif (user == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tid: String(user.id),\n\t\t\t\tname: user.name || undefined,\n\t\t\t\temail: user.publicEmail || undefined,\n\t\t\t\tavatarUrl: user.avatarUrl || undefined,\n\t\t\t\tusername: user.username || undefined,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, userId) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tuserId: userId,\n\t\t}),\n\t})\n\tasync getUserSigningKeys(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tuserId: string | number,\n\t\toptions?: { baseUrl?: string },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitLabSshKey[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\t// SSH keys are public, so this works for any user with the current token (covered by the `read_user` scope).\n\t\ttry {\n\t\t\tconst keys = await this.request<GitLabSshKey[]>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`v4/users/${userId}/keys`,\n\t\t\t\t{ method: 'GET' },\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\t// Exclude auth-only keys — only signing-capable keys belong in an allowed_signers file.\n\t\t\treturn keys?.filter(k => k.usage_type === 'signing' || k.usage_type === 'auth_and_signing') ?? [];\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return [];\n\n\t\t\tthis.handleException(ex, provider, scope);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t}),\n\t})\n\tasync getDefaultBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<DefaultBranch | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject:\n\t\t\t\t\t| {\n\t\t\t\t\t\t\trepository: { rootRef: string } | null | undefined;\n\t\t\t\t\t }\n\t\t\t\t\t| null\n\t\t\t\t\t| undefined;\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getDefaultBranch(\n\t$fullPath: ID!\n) {\n\tproject(fullPath: $fullPath) {\n\t\trepository {\n\t\t\trootRef\n\t\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${owner}/${repo}`,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tconst defaultBranch = rsp?.data?.project?.repository?.rootRef ?? undefined;\n\t\t\tif (defaultBranch == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tname: defaultBranch,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, number) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tnumber: number,\n\t\t}),\n\t})\n\tasync getIssueOrPullRequest(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tnumber: number,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: {\n\t\t\t\t\tmergeRequest: GitLabMergeRequest | null;\n\t\t\t\t\tissue: GitLabIssue | null;\n\t\t\t\t} | null;\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getIssueOrMergeRequest(\n\t$fullPath: ID!\n\t$iid: String!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tmergeRequest(iid: $iid) {\n\t\t\tauthor {\n\t\t\t\tid\n\t\t\t\tname\n\t\t\t\tavatarUrl\n\t\t\t\twebUrl\n\t\t\t}\n\t\t\tiid\n\t\t\ttitle\n\t\t\tdescription\n\t\t\tstate\n\t\t\tcreatedAt\n\t\t\tupdatedAt\n\t\t\tmergedAt\n\t\t\twebUrl\n\t\t}\n\t\tissue(iid: $iid) {\n\t\t\tauthor {\n\t\t\t\tid\n\t\t\t\tname\n\t\t\t\tavatarUrl\n\t\t\t\twebUrl\n\t\t\t}\n\t\t\tiid\n\t\t\ttitle\n\t\t\tdescription\n\t\t\tstate\n\t\t\tcreatedAt\n\t\t\tupdatedAt\n\t\t\tclosedAt\n\t\t\twebUrl\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${owner}/${repo}`,\n\t\t\t\t\tiid: String(number),\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tif (rsp?.data?.project?.issue != null) {\n\t\t\t\tconst issue = rsp.data.project.issue;\n\t\t\t\treturn {\n\t\t\t\t\tprovider: provider,\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tid: issue.iid,\n\t\t\t\t\tnodeId: undefined,\n\t\t\t\t\tcreatedDate: new Date(issue.createdAt),\n\t\t\t\t\tupdatedDate: new Date(issue.updatedAt),\n\t\t\t\t\ttitle: issue.title,\n\t\t\t\t\tclosed: issue.state === 'closed',\n\t\t\t\t\tclosedDate: issue.closedAt == null ? undefined : new Date(issue.closedAt),\n\t\t\t\t\turl: issue.webUrl,\n\t\t\t\t\tstate: issue.state === 'locked' ? 'closed' : issue.state,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rsp?.data?.project?.mergeRequest != null) {\n\t\t\t\tconst mergeRequest = rsp.data.project.mergeRequest;\n\t\t\t\treturn {\n\t\t\t\t\tprovider: provider,\n\t\t\t\t\ttype: 'pullrequest',\n\t\t\t\t\tid: mergeRequest.iid,\n\t\t\t\t\tnodeId: undefined,\n\t\t\t\t\tcreatedDate: new Date(mergeRequest.createdAt),\n\t\t\t\t\tupdatedDate: new Date(mergeRequest.updatedAt),\n\t\t\t\t\ttitle: mergeRequest.title,\n\t\t\t\t\tclosed: mergeRequest.state === 'closed',\n\t\t\t\t\t// TODO@eamodio this isn't right, but GitLab doesn't seem to provide a closedAt on merge requests in GraphQL\n\t\t\t\t\tclosedDate: mergeRequest.state === 'closed' ? new Date(mergeRequest.updatedAt) : undefined,\n\t\t\t\t\turl: mergeRequest.webUrl,\n\t\t\t\t\tstate: mergeRequest.state === 'locked' ? 'closed' : mergeRequest.state,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * Whether `owner/repo` has issue `iid`. Strict like {@link getPullRequest}'s `strict`: `false` only for a `null`\n\t * project or issue, which is how GitLab's GraphQL answers a missing one; GraphQL `errors`, an empty response and\n\t * any HTTP error (a 404 means a wrong endpoint) all throw instead of reading as absent.\n\t */\n\t@trace({\n\t\targs: (provider, token, owner, repo, iid) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tiid: iid,\n\t\t}),\n\t})\n\tasync hasIssue(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tiid: number,\n\t\toptions: { baseUrl?: string; deferFailure?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<boolean> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: { project: { issue: { iid: string } | null } | null } | null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query hasIssue(\n\t$fullPath: ID!\n\t$iid: String!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tissue(iid: $iid) {\n\t\t\tiid\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{ fullPath: `${owner}/${repo}`, iid: String(iid) },\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions.deferFailure,\n\t\t\t);\n\t\t\tif (rsp?.data == null) throw new Error('GitLab returned no data for the issue');\n\n\t\t\treturn rsp.data.project?.issue != null;\n\t\t} catch (ex) {\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, branch) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tbranch: branch,\n\t\t}),\n\t})\n\tasync getPullRequestForBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tbranch: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t\tinclude?: GitLabMergeRequestState[];\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: {\n\t\t\t\t\tmergeRequests?: {\n\t\t\t\t\t\tnodes: GitLabMergeRequest[];\n\t\t\t\t\t} | null;\n\t\t\t\t\topened?: {\n\t\t\t\t\t\tnodes: GitLabMergeRequest[];\n\t\t\t\t\t} | null;\n\t\t\t\t\tclosed?: {\n\t\t\t\t\t\tnodes: GitLabMergeRequest[];\n\t\t\t\t\t} | null;\n\t\t\t\t\tmerged?: {\n\t\t\t\t\t\tnodes: GitLabMergeRequest[];\n\t\t\t\t\t} | null;\n\t\t\t\t} | null;\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst fragment = `\n\t\t\tnodes {\n\t\t\t\tiid\n\t\t\t\tauthor {\n\t\t\t\t\tid\n\t\t\t\t\tname\n\t\t\t\t\tavatarUrl\n\t\t\t\t\twebUrl\n\t\t\t\t}\n\t\t\t\ttitle\n\t\t\t\tdescription\n\t\t\t\tstate\n\t\t\t\tcreatedAt\n\t\t\t\tupdatedAt\n\t\t\t\tmergedAt\n\t\t\t\twebUrl\n\t\t\t}`;\n\n\t\t\tconst query = `query getMergeRequestForBranch(\n\t$fullPath: ID!\n\t$branches: [String!]\n) {\n\tproject(fullPath: $fullPath) {\n\t\t${\n\t\t\toptions?.include == null\n\t\t\t\t? `mergeRequests(sourceBranches: $branches sort: UPDATED_DESC first: 1) {\n\t\t\t${fragment}\n\t\t}`\n\t\t\t\t: ''\n\t\t}\n\t\t${\n\t\t\toptions?.include?.includes('opened')\n\t\t\t\t? `opened: mergeRequests(sourceBranches: $branches state: opened sort: UPDATED_DESC first: 1) {\n\t\t\t${fragment}\n\t\t}`\n\t\t\t\t: ''\n\t\t}\n\t\t${\n\t\t\toptions?.include?.includes('merged')\n\t\t\t\t? `merged: mergeRequests(sourceBranches: $branches state: merged sort: UPDATED_DESC first: 1) {\n\t\t\t${fragment}\n\t\t}`\n\t\t\t\t: ''\n\t\t}\n\t\t${\n\t\t\toptions?.include?.includes('closed')\n\t\t\t\t? `closed: mergeRequests(sourceBranches: $branches state: closed sort: UPDATED_DESC first: 1) {\n\t\t\t${fragment}\n\t\t}`\n\t\t\t\t: ''\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${owner}/${repo}`,\n\t\t\t\t\tbranches: [branch],\n\t\t\t\t\tstate: options?.include,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tlet pr: GitLabMergeRequest | undefined;\n\n\t\t\tif (options?.include == null) {\n\t\t\t\tpr = rsp?.data?.project?.mergeRequests?.nodes?.[0];\n\t\t\t} else {\n\t\t\t\tfor (const state of options.include) {\n\t\t\t\t\tlet mr;\n\t\t\t\t\tif (state === 'opened') {\n\t\t\t\t\t\tmr = rsp?.data?.project?.opened?.nodes?.[0];\n\t\t\t\t\t} else if (state === 'merged') {\n\t\t\t\t\t\tmr = rsp?.data?.project?.merged?.nodes?.[0];\n\t\t\t\t\t} else if (state === 'closed') {\n\t\t\t\t\t\tmr = rsp?.data?.project?.closed?.nodes?.[0];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mr != null && (pr == null || new Date(mr.updatedAt) > new Date(pr.updatedAt))) {\n\t\t\t\t\t\tpr = mr;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (pr == null) return undefined;\n\n\t\t\treturn new PullRequest(\n\t\t\t\tprovider,\n\t\t\t\t{\n\t\t\t\t\tid: buildGitLabUserId(pr.author?.id) ?? '',\n\t\t\t\t\t// An absent author stays absent: no `'Unknown'` name a consumer can't tell from a real one, and no\n\t\t\t\t\t// `''` url/avatar that passes a presence check and renders as a link to nowhere. Matches\n\t\t\t\t\t// `fromProviderAccount`/`toIssueShape`.\n\t\t\t\t\tname: pr.author?.name ?? undefined,\n\t\t\t\t\tavatarUrl: pr.author?.avatarUrl ?? undefined,\n\t\t\t\t\turl: pr.author?.webUrl ?? undefined,\n\t\t\t\t},\n\t\t\t\t// oxlint-disable-next-line typescript/no-unnecessary-type-conversion\n\t\t\t\tString(pr.iid),\n\t\t\t\tundefined,\n\t\t\t\tpr.title,\n\t\t\t\tpr.webUrl,\n\t\t\t\t{ owner: owner, repo: repo },\n\t\t\t\tfromGitLabMergeRequestState(pr.state),\n\t\t\t\tnew Date(pr.createdAt),\n\t\t\t\tnew Date(pr.updatedAt),\n\t\t\t\t// TODO@eamodio this isn't right, but GitLab doesn't seem to provide a closedAt on merge requests in GraphQL\n\t\t\t\tpr.state !== 'closed' ? undefined : new Date(pr.updatedAt),\n\t\t\t\tpr.mergedAt == null ? undefined : new Date(pr.mergedAt),\n\t\t\t\tundefined, // mergeableState\n\t\t\t\tundefined, // viewerCanUpdate\n\t\t\t\tundefined, // refs\n\t\t\t\tundefined, // isDraft\n\t\t\t\tundefined, // additions\n\t\t\t\tundefined, // deletions\n\t\t\t\tundefined, // commentsCount\n\t\t\t\tundefined, // thumbsUpCount\n\t\t\t\tundefined, // reviewDecision\n\t\t\t\tundefined, // reviewRequests\n\t\t\t\tundefined, // latestReviews\n\t\t\t\tundefined, // assignees\n\t\t\t\tundefined, // statusCheckRollupState\n\t\t\t\tundefined, // project\n\t\t\t\tundefined, // version\n\t\t\t\tundefined, // commitCount\n\t\t\t\tundefined, // stack\n\t\t\t\tundefined, // filesChanged\n\t\t\t\tundefined, // body\n\t\t\t\tundefined, // number\n\t\t\t\tundefined, // authoredByMe\n\t\t\t\t'point',\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, rev) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\trev: rev,\n\t\t}),\n\t})\n\tasync getPullRequestForCommit(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst projectId = await this.getProjectId(provider, token, owner, repo, options?.baseUrl, cancellation);\n\t\tif (!projectId) return undefined;\n\n\t\ttry {\n\t\t\tconst mrs = await this.request<GitLabMergeRequestREST[]>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`v4/projects/${projectId}/repository/commits/${rev}/merge_requests`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t// ...options,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tif (mrs == null || mrs.length === 0) return undefined;\n\n\t\t\tif (mrs.length > 1) {\n\t\t\t\tmrs.sort(\n\t\t\t\t\t(a, b) =>\n\t\t\t\t\t\t(a.state === 'opened' ? -1 : 1) - (b.state === 'opened' ? -1 : 1) ||\n\t\t\t\t\t\tnew Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn fromGitLabMergeRequestREST(mrs[0], provider, { owner: owner, repo: repo }, 'point');\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, id) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tid: id,\n\t\t}),\n\t})\n\tasync getPullRequest(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tid: number,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\t/**\n\t\t\t * Returns `undefined` only for a `null` project or merge request, which is how GitLab's GraphQL\n\t\t\t * answers a missing one. A thrown not-found or an empty response means the endpoint is wrong, so\n\t\t\t * both throw instead of reading as absent.\n\t\t\t */\n\t\t\tstrict?: boolean;\n\t\t\tdeferFailure?: boolean;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: {\n\t\t\t\t\tmergeRequest: GitLabMergeRequestFull | null;\n\t\t\t\t} | null;\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getMergeRequest(\n\t$fullPath: ID!\n\t$iid: String!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tmergeRequest(iid: $iid) {\n\t\t\tid,\n\t\t\tiid\n\t\t\tstate,\n\t\t\tauthor {\n\t\t\t\tid\n\t\t\t\tname\n\t\t\t\tavatarUrl\n\t\t\t\twebUrl\n\t\t\t}\n\t\t\tdiffRefs {\n\t\t\t\tbaseSha\n\t\t\t\theadSha\n\t\t\t}\n\t\t\ttitle\n\t\t\tdescription\n\t\t\twebUrl\n\t\t\tcreatedAt\n\t\t\tupdatedAt\n\t\t\tmergedAt\n\t\t\ttargetBranch\n\t\t\tsourceBranch\n\t\t\tproject {\n\t\t\t\tid\n\t\t\t\tfullPath\n\t\t\t\twebUrl\n\t\t\t}\n\t\t\tsourceProject {\n\t\t\t\tid\n\t\t\t\tfullPath\n\t\t\t\twebUrl\n\t\t\t}\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${owner}/${repo}`,\n\t\t\t\t\tiid: String(id),\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions?.deferFailure,\n\t\t\t);\n\n\t\t\tif (rsp?.data == null) {\n\t\t\t\tif (options?.strict) throw new Error('GitLab returned no data for the merge request');\n\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tif (rsp.data.project?.mergeRequest == null) return undefined;\n\n\t\t\tconst pr = rsp.data.project.mergeRequest;\n\t\t\treturn fromGitLabMergeRequest(pr, provider, 'point');\n\t\t} catch (ex) {\n\t\t\tif (!options?.strict && ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * The iids of the merge requests into `owner/repo` whose source is `branch`, in any state, newest first, at most\n\t * `limit`: from the project itself when `headOwner` is omitted, otherwise from a fork in the `headOwner`\n\t * namespace. Only the iids, so a caller resolves the rows through the same read as its by-number lookups.\n\t *\n\t * Keyed on the source branch NAME, so a merged request whose branch was deleted still matches. Strict like\n\t * {@link getPullRequest}'s `strict`: an empty list means GitLab answered, a `null` project included; a 404 or\n\t * an empty response means the endpoint is wrong and throws. Requests from a deleted fork carry no source\n\t * project and so match nothing.\n\t */\n\t@trace({\n\t\targs: (provider, token, owner, repo, branch) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tbranch: branch,\n\t\t}),\n\t})\n\tasync getPullRequestNumbersForBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tbranch: string,\n\t\toptions: { baseUrl?: string; headOwner?: string; limit: number; deferFailure?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<{ numbers: number[]; truncated: boolean }> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface MergeRequestHead {\n\t\t\tiid: string;\n\t\t\tupdatedAt: string;\n\t\t\tsourceBranch: string;\n\t\t\tproject: { id: string };\n\t\t\tsourceProject: { id: string; fullPath: string } | null;\n\t\t}\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: {\n\t\t\t\t\tmergeRequests: {\n\t\t\t\t\t\tcount: number;\n\t\t\t\t\t\tpageInfo: { hasNextPage: boolean };\n\t\t\t\t\t\tnodes: MergeRequestHead[];\n\t\t\t\t\t} | null;\n\t\t\t\t} | null;\n\t\t\t} | null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getMergeRequestsForBranch(\n\t$fullPath: ID!\n\t$branches: [String!]\n\t$limit: Int!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tmergeRequests(sourceBranches: $branches, state: all, sort: UPDATED_DESC, first: $limit) {\n\t\t\tcount\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tiid\n\t\t\t\tupdatedAt\n\t\t\t\tsourceBranch\n\t\t\t\tproject {\n\t\t\t\t\tid\n\t\t\t\t}\n\t\t\t\tsourceProject {\n\t\t\t\t\tid\n\t\t\t\t\tfullPath\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{ fullPath: `${owner}/${repo}`, branches: [branch], limit: options.limit },\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions.deferFailure,\n\t\t\t);\n\t\t\tif (rsp?.data == null) throw new Error('GitLab returned no data for the merge requests by branch');\n\n\t\t\tconst project = rsp.data.project;\n\t\t\tif (project == null) return { numbers: [], truncated: false };\n\n\t\t\tconst connection = project.mergeRequests;\n\t\t\tif (connection == null) {\n\t\t\t\tthrow new Error(`GitLab returned no merge requests for ${owner}/${repo}:${branch}`);\n\t\t\t}\n\n\t\t\tconst headOwner = options.headOwner;\n\t\t\tconst { values, truncated } = selectBranchPullRequests(connection.nodes, {\n\t\t\t\tmatchesHead: mr => {\n\t\t\t\t\tif (mr.sourceBranch !== branch || mr.sourceProject == null) return false;\n\t\t\t\t\tif (headOwner == null) return mr.sourceProject.id === mr.project.id;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\tmr.sourceProject.id !== mr.project.id &&\n\t\t\t\t\t\tequalsIgnoreCase(getRepoNamespace(mr.sourceProject.fullPath), headOwner)\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tupdatedAt: mr => Date.parse(mr.updatedAt),\n\t\t\t\tmap: mr => {\n\t\t\t\t\tconst iid = Number(mr.iid);\n\t\t\t\t\tif (!Number.isSafeInteger(iid)) throw new Error(`GitLab returned a merge request iid '${mr.iid}'`);\n\n\t\t\t\t\treturn iid;\n\t\t\t\t},\n\t\t\t\tlimit: options.limit,\n\t\t\t\tmore: connection.pageInfo.hasNextPage || connection.count > connection.nodes.length,\n\t\t\t});\n\t\t\treturn { numbers: values, truncated: truncated };\n\t\t} catch (ex) {\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * The change state of merge requests `iids` in `fullPath` — the cheap check behind the batch pull request read's\n\t * etags. Selects only what provider-apis' `getPullRequestForRepo` maps into a full row's etag inputs, plus the\n\t * fields of each of `options.etagIncludes`, in ONE request for up to {@link gitLabEtagFieldsMaxIids} iids. One\n\t * entry per input iid, in order; `undefined` is a PROVEN ABSENCE (see {@link getEtagFieldsByIid}).\n\t */\n\t@trace({\n\t\targs: (provider, token, fullPath, iids) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tfullPath: fullPath,\n\t\t\tiids: iids.length,\n\t\t}),\n\t})\n\tasync getMergeRequestsEtagFields(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tfullPath: string,\n\t\tiids: readonly number[],\n\t\toptions: { baseUrl?: string; etagIncludes: readonly PullRequestEtagInclude[]; deferFailure?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<(GitLabMergeRequestEtagNode | undefined)[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst selections = ['iid', 'state', 'draft', 'updatedAt', 'diffRefs { headSha }'];\n\t\tif (options.etagIncludes.includes('mergeable')) {\n\t\t\tselections.push('mergeStatusEnum');\n\t\t}\n\t\tif (options.etagIncludes.includes('reviewDecision')) {\n\t\t\tselections.push('reviewers { nodes { mergeRequestInteraction { reviewState } } }');\n\t\t}\n\t\tif (options.etagIncludes.includes('checks')) {\n\t\t\tselections.push('headPipeline { stages { nodes { jobs { nodes { status allowFailure } } } } }');\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getMergeRequestsEtagFields(\n\t$fullPath: ID!\n\t$iids: [String!]\n\t$first: Int!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tmergeRequests(iids: $iids, state: all, first: $first) {\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\t${selections.join('\\n\\t\\t\\t\\t')}\n\t\t\t}\n\t\t}\n\t}\n}`;\n\n\t\t\treturn await this.getEtagFieldsByIid<GitLabMergeRequestEtagNode>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t'mergeRequests',\n\t\t\t\tfullPath,\n\t\t\t\tiids,\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions.deferFailure,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * The change state of issues `iids` in `fullPath` — the cheap check behind the batch issue read's etags — selecting\n\t * only what provider-apis' issue read maps into a full row's etag inputs, plus the field of each of\n\t * `options.etagIncludes`, in ONE request for up to {@link gitLabEtagFieldsMaxIids} iids. One entry per input iid,\n\t * in order; `undefined` is a PROVEN ABSENCE (see {@link getEtagFieldsByIid}).\n\t */\n\t@trace({\n\t\targs: (provider, token, fullPath, iids) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tfullPath: fullPath,\n\t\t\tiids: iids.length,\n\t\t}),\n\t})\n\tasync getIssuesEtagFields(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tfullPath: string,\n\t\tiids: readonly number[],\n\t\toptions: { baseUrl?: string; etagIncludes: readonly IssueEtagInclude[]; deferFailure?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<(GitLabIssueEtagNode | undefined)[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst selections = ['iid', 'closedAt', 'updatedAt'];\n\t\tif (options.etagIncludes.includes('reactions')) {\n\t\t\tselections.push('upvotes');\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getIssuesEtagFields(\n\t$fullPath: ID!\n\t$iids: [String!]\n\t$first: Int!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tissues(iids: $iids, state: all, first: $first) {\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\t${selections.join('\\n\\t\\t\\t\\t')}\n\t\t\t}\n\t\t}\n\t}\n}`;\n\n\t\t\treturn await this.getEtagFieldsByIid<GitLabIssueEtagNode>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t'issues',\n\t\t\t\tfullPath,\n\t\t\t\tiids,\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions.deferFailure,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * Runs an etag read's `query` and maps its `connection`'s nodes back to `iids`, positionally.\n\t *\n\t * Strict like {@link getPullRequest}'s `strict`: an iid is absent only when the project is `null`, or when it is\n\t * missing from a complete page of an error-free reply — the answers that prove a miss to the full read's own\n\t * confirming read. GraphQL `errors`, an empty response, a `null` connection, a page that has more and any HTTP\n\t * error (a 404 means a wrong endpoint) throw instead. The query asks for `state: all`, so no default state filter\n\t * can hide a closed or merged item and have it read as missing.\n\t */\n\tprivate async getEtagFieldsByIid<T extends { iid: string }>(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string | undefined,\n\t\tquery: string,\n\t\tconnection: 'mergeRequests' | 'issues',\n\t\tfullPath: string,\n\t\tiids: readonly number[],\n\t\tcancellation: AbortSignal | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tdeferFailure: boolean | undefined,\n\t): Promise<(T | undefined)[]> {\n\t\tif (!iids.length) return [];\n\t\tif (iids.length > gitLabEtagFieldsMaxIids) {\n\t\t\tthrow new Error(`Cannot read more than ${gitLabEtagFieldsMaxIids} GitLab iids in one request`);\n\t\t}\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: Partial<\n\t\t\t\t\tRecord<'mergeRequests' | 'issues', { pageInfo: { hasNextPage: boolean }; nodes: T[] } | null>\n\t\t\t\t> | null;\n\t\t\t} | null;\n\t\t}\n\n\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\tquery,\n\t\t\t{ fullPath: fullPath, iids: iids.map(String), first: iids.length },\n\t\t\tcancellation,\n\t\t\tscope,\n\t\t\tdeferFailure,\n\t\t);\n\t\tif (rsp?.data == null) throw new Error(`GitLab returned no data for the ${connection} of ${fullPath}`);\n\n\t\tconst project = rsp.data.project;\n\t\tif (project == null) return iids.map(() => undefined);\n\n\t\tconst page = project[connection];\n\t\tif (page?.nodes == null) throw new Error(`GitLab returned no ${connection} for ${fullPath}`);\n\t\tif (page.pageInfo.hasNextPage) throw new Error(`GitLab returned a partial page of ${connection}`);\n\n\t\tconst byIid = new Map(page.nodes.map(node => [node.iid, node]));\n\t\treturn iids.map(iid => byIid.get(String(iid)));\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t}),\n\t})\n\tasync getRepositoryMetadata(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst projectId = await this.getProjectId(provider, token, owner, repo, options?.baseUrl, cancellation);\n\t\tif (!projectId) return undefined;\n\n\t\ttry {\n\t\t\tconst proj = await this.request<GitLabProjectREST>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`v4/projects/${projectId}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t// ...options,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tif (proj == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\towner: proj.namespace.full_path,\n\t\t\t\tname: proj.path,\n\t\t\t\tisFork: proj.forked_from_project != null,\n\t\t\t\tparent:\n\t\t\t\t\tproj.forked_from_project != null\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\towner: proj.forked_from_project.namespace.full_path,\n\t\t\t\t\t\t\t\tname: proj.forked_from_project.path,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t} satisfies RepositoryMetadata;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({ args: (provider, token) => ({ provider: provider.name, token: `<token:${token.microHash}>` }) })\n\tasync searchPullRequests(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\toptions?: {\n\t\t\tsearch?: string;\n\t\t\tuser?: string;\n\t\t\trepos?: string[];\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t\tinclude?: PullRequestState[];\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest[]> {\n\t\tconst scope = getScopedLogger();\n\t\tconst search = options?.search;\n\t\tif (!search) {\n\t\t\treturn [];\n\t\t}\n\n\t\ttry {\n\t\t\tconst perPageLimit = 20; // with bigger amount we exceed the max GraphQL complexity in the next query\n\t\t\tconst include = options?.include?.length ? options.include : undefined;\n\t\t\t// GitLab's search API takes a single `state` value, so push it server-side when exactly one state is\n\t\t\t// requested; that keeps the requested state from being crowded out of the `perPageLimit` slice. For\n\t\t\t// multiple states there's no single-value qualifier, so those are filtered client-side below.\n\t\t\tlet searchPath = `v4/search/?scope=merge_requests&search=${encodeURIComponent(search)}&per_page=${perPageLimit}`;\n\t\t\tif (include?.length === 1) {\n\t\t\t\tsearchPath += `&state=${encodeURIComponent(toGitLabMergeRequestState(include[0]))}`;\n\t\t\t}\n\n\t\t\tlet restPRs: GitLabMergeRequestREST[];\n\t\t\tif (include == null) {\n\t\t\t\t// Unfiltered search returns the first page as-is (as it did before state filtering existed).\n\t\t\t\trestPRs = await this.request<GitLabMergeRequestREST[]>(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\toptions?.baseUrl,\n\t\t\t\t\tsearchPath,\n\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\tcancellation,\n\t\t\t\t\tscope,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// The search API returns MRs of all states in relevance order, so the requested state(s) may not\n\t\t\t\t// appear on the first page. Server-side `&state=` narrows the single-state case when honored, but it\n\t\t\t\t// can't express multiple states, so page through the results and accumulate matches until we fill the\n\t\t\t\t// detail-query cap (`perPageLimit`) or exhaust the search results. This keeps correctness whether\n\t\t\t\t// or not `&state=` takes effect. Bounded by `maxSearchPages` like the other paged provider drains in\n\t\t\t\t// this package so a large, low-match result set can't fan out into an unbounded request loop.\n\t\t\t\tconst maxSearchPages = 20;\n\t\t\t\tconst matches: GitLabMergeRequestREST[] = [];\n\t\t\t\tfor (let page = 1; page <= maxSearchPages; page++) {\n\t\t\t\t\tconst pagePRs = await this.request<GitLabMergeRequestREST[]>(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\ttoken,\n\t\t\t\t\t\toptions?.baseUrl,\n\t\t\t\t\t\t`${searchPath}&page=${page}`,\n\t\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\t\tcancellation,\n\t\t\t\t\t\tscope,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const pr of pagePRs) {\n\t\t\t\t\t\tif (include.includes(fromGitLabMergeRequestState(pr.state))) {\n\t\t\t\t\t\t\tmatches.push(pr);\n\t\t\t\t\t\t\tif (matches.length >= perPageLimit) break;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Stop once we've filled the cap or hit the last (partial or empty) page.\n\t\t\t\t\tif (matches.length >= perPageLimit || pagePRs.length < perPageLimit) break;\n\t\t\t\t}\n\t\t\t\trestPRs = matches;\n\t\t\t}\n\t\t\tif (restPRs.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tinterface QueryResult {\n\t\t\t\tdata: Record<`mergeRequest_${number}`, GitLabMergeRequestFull>;\n\t\t\t}\n\n\t\t\tconst queryArgs = restPRs.map((_, i) => `$id_${i}: MergeRequestID!`).join('\\n');\n\t\t\tconst queryFields = restPRs\n\t\t\t\t.map((_, i) => `mergeRequest_${i}: mergeRequest(id: $id_${i}) { ...mergeRequestFields }`)\n\t\t\t\t.join('\\n');\n\t\t\t// Set of fields includes only additional fields that are not included in GitLabMergeRequestREST.\n\t\t\t// It's limited as much as possible to reduce complexity of the query.\n\t\t\tconst queryMrFields = `fragment mergeRequestFields on MergeRequest {\n\t\t\t\tdiffRefs {\n\t\t\t\t\tbaseSha\n\t\t\t\t\theadSha\n\t\t\t\t}\n\t\t\t\tproject {\n\t\t\t\t\tid\n\t\t\t\t\tfullPath\n\t\t\t\t\twebUrl\n\t\t\t\t}\n\t\t\t\tsourceProject {\n\t\t\t\t\tid\n\t\t\t\t\tfullPath\n\t\t\t\t\twebUrl\n\t\t\t\t}\n\t\t\t}`;\n\t\t\tconst query = `query getMergeRequests (${queryArgs}) {${queryFields}} ${queryMrFields}`;\n\n\t\t\tconst params = restPRs.reduce<Record<`id_${number}`, string>>((ids, gitlabRestPr, i) => {\n\t\t\t\tids[`id_${i}`] = `${gitlabMergeRequestIdPrefix}${gitlabRestPr.id}`;\n\t\t\t\treturn ids;\n\t\t\t}, {});\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\tparams,\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tif (rsp?.data != null) {\n\t\t\t\tconst resultPRs = restPRs.reduce<PullRequest[]>((accum, restPR, i) => {\n\t\t\t\t\tconst graphQlPR = rsp.data[`mergeRequest_${i}`];\n\t\t\t\t\tif (graphQlPR == null) {\n\t\t\t\t\t\treturn accum;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fullPr: GitLabMergeRequestFull = {\n\t\t\t\t\t\t...graphQlPR,\n\t\t\t\t\t\tiid: String(restPR.iid),\n\t\t\t\t\t\tid: String(restPR.id),\n\t\t\t\t\t\tstate: restPR.state,\n\t\t\t\t\t\t// An absent REST author stays absent (`null`, as GitLab's own shape expresses it) rather than a\n\t\t\t\t\t\t// synthesized `'Unknown'` stub: `fromGitLabMergeRequest` collapses `null` to an absent\n\t\t\t\t\t\t// `PullRequestMember.name`, whereas a placeholder here would launder into a real-looking name.\n\t\t\t\t\t\tauthor:\n\t\t\t\t\t\t\trestPR.author != null\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\tid: buildGitLabUserId(restPR.author.id) ?? '',\n\t\t\t\t\t\t\t\t\t\tname: restPR.author.name,\n\t\t\t\t\t\t\t\t\t\tavatarUrl: restPR.author.avatar_url ?? null,\n\t\t\t\t\t\t\t\t\t\twebUrl: restPR.author.web_url,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\ttitle: restPR.title,\n\t\t\t\t\t\tdescription: restPR.description,\n\t\t\t\t\t\twebUrl: restPR.web_url,\n\t\t\t\t\t\tcreatedAt: restPR.created_at,\n\t\t\t\t\t\tupdatedAt: restPR.updated_at,\n\t\t\t\t\t\tmergedAt: restPR.merged_at,\n\t\t\t\t\t\tsourceBranch: restPR.source_branch,\n\t\t\t\t\t\ttargetBranch: restPR.target_branch,\n\t\t\t\t\t};\n\t\t\t\t\taccum.push(fromGitLabMergeRequest(fullPr, provider, 'text-search'));\n\t\t\t\t\treturn accum;\n\t\t\t\t}, []);\n\t\t\t\treturn resultPRs;\n\t\t\t}\n\t\t\treturn [];\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return [];\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\tprivate async findUser(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tsearch: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitLabUser[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tusers: {\n\t\t\t\t\tnodes: {\n\t\t\t\t\t\tid: string;\n\t\t\t\t\t\tname: string;\n\t\t\t\t\t\tusername: string;\n\t\t\t\t\t\tpublicEmail?: string;\n\t\t\t\t\t\tstate: string;\n\t\t\t\t\t\tavatarUrl: string;\n\t\t\t\t\t\twebUrl: string;\n\t\t\t\t\t}[];\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query findUser(\n$search: String!\n) {\n\tusers(search: $search) {\n\t\tnodes {\n\t\t\tid\n\t\t\tname\n\t\t\tusername,\n\t\t\tpublicEmail,\n\t\t\tstate\n\t\t\tavatarUrl\n\t\t\twebUrl\n\t\t}\n\t}\n}`;\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tsearch: search,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tconst matches = rsp?.data?.users?.nodes;\n\t\t\tif (matches == null || matches.length === 0) return [];\n\n\t\t\tconst users: GitLabUser[] = [];\n\n\t\t\tfor (const user of matches) {\n\t\t\t\tconst match = /gid:\\/\\/gitlab\\/User\\/([0-9]+)\\b/.exec(user.id);\n\t\t\t\tif (match == null) continue;\n\n\t\t\t\tusers.push({\n\t\t\t\t\tid: parseInt(match[1], 10),\n\t\t\t\t\tname: user.name,\n\t\t\t\t\tusername: user.username,\n\t\t\t\t\tpublicEmail: user.publicEmail || undefined,\n\t\t\t\t\tstate: user.state,\n\t\t\t\t\tavatarUrl: user.avatarUrl,\n\t\t\t\t\twebUrl: user.webUrl,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return [];\n\n\t\t\tthis.handleException(ex, provider, scope);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tgetProjectId(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tgroup: string,\n\t\trepo: string,\n\t\tbaseUrl: string | undefined,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<string | undefined> {\n\t\tconst { accessToken } = token;\n\t\tconst key = `${accessToken}|${group}/${repo}`;\n\n\t\tlet projectId = this._projectIds.get(key);\n\t\tif (projectId == null) {\n\t\t\tprojectId = this.getProjectIdCore(provider, token, group, repo, baseUrl, cancellation);\n\t\t\tthis._projectIds.set(key, projectId);\n\t\t}\n\n\t\treturn projectId;\n\t}\n\n\tprivate async getProjectIdCore(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tgroup: string,\n\t\trepo: string,\n\t\tbaseUrl: string | undefined,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<string | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: { project: { id: string } };\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getProjectId(\n\t$fullPath: ID!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tid\n\t}\n}`;\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${group}/${repo}`,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tconst gid = rsp?.data?.project?.id;\n\t\t\tif (gid == null) return undefined;\n\n\t\t\tconst match = /gid:\\/\\/gitlab\\/Project\\/([0-9]+)\\b/.exec(gid);\n\t\t\tif (match == null) return undefined;\n\n\t\t\tconst projectId = match[1];\n\n\t\t\tscope?.addExitInfo(`projectId=${projectId}`);\n\t\t\treturn projectId;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthis.handleException(ex, provider, scope);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async graphql<T extends object>(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string | undefined,\n\t\tquery: string,\n\t\tvariables: Record<string, any>,\n\t\tcancellation: AbortSignal | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tdeferFailure?: boolean,\n\t): Promise<T | undefined> {\n\t\tconst { accessToken } = token;\n\t\tlet rsp: Response;\n\t\ttry {\n\t\t\tconst sw = maybeStopWatch(`[GITLAB] POST ${baseUrl}`, { log: { onlyExit: true } });\n\n\t\t\ttry {\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\trsp = await this.config.wrapForForcedInsecureSSL(provider.getIgnoreSSLErrors(), () =>\n\t\t\t\t\tthis.config.fetch(`${baseUrl ?? 'https://gitlab.com/api'}/graphql`, {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n\t\t\t\t\t\tsignal: cancellation,\n\t\t\t\t\t\tbody: JSON.stringify({ query: query, variables: variables }),\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\tif (rsp.ok) {\n\t\t\t\t\tconst data = (await rsp.json()) as T | { errors: { message: string }[] };\n\n\t\t\t\t\tif ('errors' in data) throw new ProviderFetchError('GitLab', rsp, data.errors);\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// Reads the body so the 403 branch below can tell a throttled request from a permission failure.\n\t\t\t\tthrow await ProviderFetchError.fromResponse('GitLab', rsp);\n\t\t\t} finally {\n\t\t\t\tconst match = /(^[^({\\n]+)/.exec(query);\n\t\t\t\tconst message = ` ${match?.[1].trim() ?? query}`;\n\n\t\t\t\tsw?.stop({ message: message });\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof ProviderFetchError || ex.name === 'AbortError') {\n\t\t\t\tthis.handleRequestError(provider, token, ex, scope, deferFailure);\n\t\t\t} else if (Logger.isDebugging) {\n\t\t\t\tthis.config.onError?.(`GitLab request failed: ${ex.message}`);\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate async request<T>(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string | undefined,\n\t\troute: string,\n\t\toptions: { method: RequestInit['method'] } & Record<string, unknown>,\n\t\tcancellation: AbortSignal | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t): Promise<T> {\n\t\tconst { accessToken } = token;\n\t\tconst url = `${baseUrl ?? 'https://gitlab.com/api'}/${route}`;\n\n\t\tlet rsp: Response;\n\t\ttry {\n\t\t\tconst sw = maybeStopWatch(`[GITLAB] ${options?.method ?? 'GET'} ${url}`, { log: { onlyExit: true } });\n\n\t\t\ttry {\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\trsp = await this.config.wrapForForcedInsecureSSL(provider.getIgnoreSSLErrors(), () =>\n\t\t\t\t\tthis.config.fetch(url, {\n\t\t\t\t\t\theaders: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n\t\t\t\t\t\tsignal: cancellation,\n\t\t\t\t\t\t...options,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\tif (rsp.ok) {\n\t\t\t\t\treturn (await rsp.json()) as T;\n\t\t\t\t}\n\n\t\t\t\t// Reads the body so the 403 branch below can tell a throttled request from a permission failure.\n\t\t\t\tthrow await ProviderFetchError.fromResponse('GitLab', rsp);\n\t\t\t} finally {\n\t\t\t\tsw?.stop();\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof ProviderFetchError || ex.name === 'AbortError') {\n\t\t\t\tthis.handleRequestError(provider, token, ex, scope, undefined);\n\t\t\t} else if (Logger.isDebugging) {\n\t\t\t\tthis.config.onError?.(`GitLab request failed: ${ex.message}`);\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate handleRequestError(\n\t\tprovider: Provider | undefined,\n\t\ttoken: TokenWithInfo,\n\t\tex: ProviderFetchError | (Error & { name: 'AbortError' }),\n\t\tscope: ScopedLogger | undefined,\n\t\tdeferFailure: boolean | undefined,\n\t): void {\n\t\tif (ex.name === 'AbortError' || !(ex instanceof ProviderFetchError)) throw new CancellationError(ex);\n\n\t\tconst { accessToken, ...tokenInfo } = token;\n\t\tswitch (ex.status) {\n\t\t\tcase 404: // Not found\n\t\t\tcase 410: // Gone\n\t\t\tcase 422: // Unprocessable Entity\n\t\t\t\tthrow new RequestNotFoundError(ex);\n\t\t\tcase 429: // Too Many Requests\n\t\t\t\tthrow toRateLimitError(ex, accessToken);\n\t\t\tcase 401: // Unauthorized\n\t\t\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Unauthorized, ex);\n\t\t\tcase 403: // Forbidden\n\t\t\t\t// GitLab returns 403 for both a permission failure and a throttled request, so the message is the\n\t\t\t\t// discriminant (see `isRateLimitResponse`, shared with Bitbucket/Azure).\n\t\t\t\tif (isRateLimitResponse(ex)) throw toRateLimitError(ex, accessToken);\n\n\t\t\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Forbidden, ex);\n\t\t\tcase 500: // Internal Server Error\n\t\t\t\tscope?.error(ex);\n\t\t\t\tif (ex.response != null) {\n\t\t\t\t\treportRequestFailure(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\tex,\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\tthis.config.onRequestFailed?.(\n\t\t\t\t\t\t\t\tprovider == null || provider.id === 'gitlab'\n\t\t\t\t\t\t\t\t\t? l10n.t(\n\t\t\t\t\t\t\t\t\t\t\t'{0} failed to respond and might be experiencing issues. Please visit the [GitLab status page](https://status.gitlab.com) for more information.',\n\t\t\t\t\t\t\t\t\t\t\tprovider?.name ?? 'GitLab',\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t: l10n.t('{0} failed to respond and might be experiencing issues.', provider.name),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tdeferFailure,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 502: // Bad Gateway\n\t\t\t\tscope?.error(ex);\n\t\t\t\t// GitHub seems to return this status code for timeouts\n\t\t\t\tif (ex.message.includes('timeout')) {\n\t\t\t\t\treportRequestFailure(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\tex,\n\t\t\t\t\t\t() => this.config.onRequestTimedOut?.(provider?.name ?? 'GitLab'),\n\t\t\t\t\t\tdeferFailure,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (ex.status >= 400 && ex.status < 500) throw new RequestClientError(ex);\n\t\t\t\tbreak;\n\t\t}\n\n\t\tscope?.error(ex);\n\t\tif (Logger.isDebugging) {\n\t\t\tthis.config.onError?.(`GitLab request failed: ${(ex.response as any)?.errors?.[0]?.message ?? ex.message}`);\n\t\t}\n\t}\n\n\tprivate handleException(ex: Error, provider: Provider, scope: ScopedLogger | undefined): Error {\n\t\tscope?.error(ex);\n\t\t// debugger;\n\n\t\tif (ex instanceof AuthenticationError) {\n\t\t\tvoid this.showAuthenticationErrorMessage(ex, provider);\n\t\t}\n\t\treturn ex;\n\t}\n\n\tprivate async showAuthenticationErrorMessage(ex: AuthenticationError, provider: Provider) {\n\t\tif (ex.reason === AuthenticationErrorReason.Unauthorized || ex.reason === AuthenticationErrorReason.Forbidden) {\n\t\t\tconst reauthenticate = await this.config.onReauthenticationRequired?.(\n\t\t\t\tex.reason === AuthenticationErrorReason.Forbidden\n\t\t\t\t\t? l10n.t('{0}. Would you like to try reauthenticating to provide additional access?', ex.message)\n\t\t\t\t\t: l10n.t('{0}. Would you like to try reauthenticating?', ex.message),\n\t\t\t);\n\n\t\t\tif (reauthenticate) {\n\t\t\t\tawait provider.reauthenticate();\n\t\t\t\tthis.resetCaches();\n\t\t\t}\n\t\t} else {\n\t\t\tthis.config.onError?.(ex.message);\n\t\t}\n\t}\n}\n\n/** Wires a {@link GitLabApi} from the full runtime context, mapping `ctx` down to the narrow config. */\nexport function createGitLabApi(ctx: IntegrationServiceContext): GitLabApi {\n\tconst config: ProviderApiConfig = {\n\t\t...baseProviderApiConfig(ctx),\n\t\t// Self-hosted GitLab keys its API base off `remotes`, so reset on both HTTP proxy and remotes changes.\n\t\tonConfigChanged: listener =>\n\t\t\tctx.config.onDidChange(e => {\n\t\t\t\tif (e.httpProxy || e.remotes) {\n\t\t\t\t\tlistener();\n\t\t\t\t}\n\t\t\t}),\n\t\tonReauthenticationRequired: message =>\n\t\t\tctx.hooks?.onReauthenticationRequired?.(message) ?? Promise.resolve(false),\n\t\tonRequestTimedOut: providerName => ctx.hooks?.ui?.onRequestTimedOut?.(providerName),\n\t};\n\n\treturn new GitLabApi(config);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gitlab.js","names":["Logger"],"sources":["../../../../../../plus/integrations/src/providers/gitlab/gitlab.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport { reportRequestFailure } from '@gitlens/git/errors.js';\nimport type { Account } from '@gitlens/git/models/author.js';\nimport type { DefaultBranch } from '@gitlens/git/models/defaultBranch.js';\nimport type { IssueOrPullRequest } from '@gitlens/git/models/issueOrPullRequest.js';\nimport { PullRequest } from '@gitlens/git/models/pullRequest.js';\nimport type { PullRequestState } from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { RepositoryMetadata } from '@gitlens/git/models/repositoryMetadata.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport type { Disposable } from '@gitlens/utils/disposable.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { equalsIgnoreCase } from '@gitlens/utils/string.js';\nimport { joinUriPath, parseUri } from '@gitlens/utils/uri.js';\nimport type { TokenWithInfo } from '../../authentication/models.js';\nimport type { IntegrationServiceContext } from '../../context.js';\nimport {\n\tAuthenticationError,\n\tAuthenticationErrorReason,\n\tisRateLimitResponse,\n\tProviderFetchError,\n\tRequestClientError,\n\tRequestNotFoundError,\n\ttoRateLimitError,\n} from '../../errors.js';\nimport type { IssueEtagInclude, PullRequestEtagInclude } from '../../models/integration.js';\nimport type { ProviderApiConfig } from '../apiConfig.js';\nimport { baseProviderApiConfig } from '../apiConfig.js';\nimport { selectBranchPullRequests } from '../utils/providerPaging.js';\nimport { selectGitLabUserForCommit } from './gitlab.utils.js';\nimport type {\n\tGitLabCommit,\n\tGitLabIssue,\n\tGitLabIssueEtagNode,\n\tGitLabMergeRequest,\n\tGitLabMergeRequestEtagNode,\n\tGitLabMergeRequestFull,\n\tGitLabMergeRequestREST,\n\tGitLabMergeRequestState,\n\tGitLabProjectREST,\n\tGitLabSshKey,\n\tGitLabUser,\n} from './models.js';\nimport {\n\tfromGitLabMergeRequest,\n\tfromGitLabMergeRequestREST,\n\tfromGitLabMergeRequestState,\n\tgetRepoNamespace,\n\tgitLabEtagFieldsMaxIids,\n\ttoGitLabMergeRequestState,\n} from './models.js';\n\n// drop it as soon as we switch to @gitkraken/providers-api\nconst gitlabUserIdPrefix = 'gid://gitlab/User/';\nconst gitlabMergeRequestIdPrefix = 'gid://gitlab/MergeRequest/';\n\nfunction buildGitLabUserId(id: string | number | undefined): string | undefined {\n\treturn typeof id === 'string' && id?.startsWith(gitlabUserIdPrefix)\n\t\t? id.substring(gitlabUserIdPrefix.length)\n\t\t: String(id);\n}\n\nexport class GitLabApi implements Disposable {\n\tprivate readonly _disposable: Disposable | undefined;\n\tprivate _projectIds = new Map<string, Promise<string | undefined>>();\n\n\tconstructor(private readonly config: ProviderApiConfig) {\n\t\tthis._disposable = config.onConfigChanged?.(() => this.resetCaches());\n\t}\n\n\tdispose(): void {\n\t\tthis._disposable?.dispose();\n\t}\n\n\tprivate resetCaches(): void {\n\t\tthis._projectIds.clear();\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, rev) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\trev: rev,\n\t\t}),\n\t})\n\tasync getAccountForCommit(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<Account | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst projectId = await this.getProjectId(provider, token, owner, repo, options?.baseUrl, cancellation);\n\t\tif (!projectId) return undefined;\n\n\t\ttry {\n\t\t\tconst commit = await this.request<GitLabCommit>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`v4/projects/${projectId}/repository/commits/${rev}?stats=false`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t// ...options,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tlet users = await this.findUser(provider, token, commit.author_name, options, cancellation);\n\t\t\tlet user = selectGitLabUserForCommit(users, commit.author_name, commit.author_email);\n\n\t\t\t// If the name search was ambiguous (multiple users share the name), try an email-scoped search to\n\t\t\t// disambiguate before giving up — otherwise we'd show a stranger's avatar (see #2205)\n\t\t\tif (user == null && commit.author_email) {\n\t\t\t\tconst exactNameMatches = users.filter(u => equalsIgnoreCase(u.name, commit.author_name)).length;\n\t\t\t\tif (exactNameMatches > 1) {\n\t\t\t\t\tusers = await this.findUser(provider, token, commit.author_email, options, cancellation);\n\t\t\t\t\tuser = selectGitLabUserForCommit(users, commit.author_name, commit.author_email);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (user == null) return undefined;\n\n\t\t\t// If the avatarUrl is a relative URL, make it absolute using the webUrl (assuming the webUrl is the root URL with the username tacked on)\n\t\t\tif (user.avatarUrl && !/^([a-zA-Z][\\w+.-]+):/.test(user.avatarUrl)) {\n\t\t\t\tuser.avatarUrl = joinUriPath(parseUri(user.webUrl), '..', user.avatarUrl).toString();\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tid: String(user.id),\n\t\t\t\tname: user.name || undefined,\n\t\t\t\temail: commit.author_email || undefined,\n\t\t\t\tavatarUrl: user.avatarUrl || undefined,\n\t\t\t\tusername: user.username || undefined,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, _owner, _repo, email) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\temail: email,\n\t\t}),\n\t})\n\tasync getAccountForEmail(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\t_owner: string,\n\t\t_repo: string,\n\t\temail: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t},\n\t): Promise<Account | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst [user] = await this.findUser(provider, token, email, options);\n\t\t\tif (user == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tid: String(user.id),\n\t\t\t\tname: user.name || undefined,\n\t\t\t\temail: user.publicEmail || undefined,\n\t\t\t\tavatarUrl: user.avatarUrl || undefined,\n\t\t\t\tusername: user.username || undefined,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, userId) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tuserId: userId,\n\t\t}),\n\t})\n\tasync getUserSigningKeys(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tuserId: string | number,\n\t\toptions?: { baseUrl?: string },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitLabSshKey[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\t// SSH keys are public, so this works for any user with the current token (covered by the `read_user` scope).\n\t\ttry {\n\t\t\tconst keys = await this.request<GitLabSshKey[]>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`v4/users/${userId}/keys`,\n\t\t\t\t{ method: 'GET' },\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\t// Exclude auth-only keys — only signing-capable keys belong in an allowed_signers file.\n\t\t\treturn keys?.filter(k => k.usage_type === 'signing' || k.usage_type === 'auth_and_signing') ?? [];\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return [];\n\n\t\t\tthis.handleException(ex, provider, scope);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t}),\n\t})\n\tasync getDefaultBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<DefaultBranch | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject:\n\t\t\t\t\t| {\n\t\t\t\t\t\t\trepository: { rootRef: string } | null | undefined;\n\t\t\t\t\t }\n\t\t\t\t\t| null\n\t\t\t\t\t| undefined;\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getDefaultBranch(\n\t$fullPath: ID!\n) {\n\tproject(fullPath: $fullPath) {\n\t\trepository {\n\t\t\trootRef\n\t\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${owner}/${repo}`,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tconst defaultBranch = rsp?.data?.project?.repository?.rootRef ?? undefined;\n\t\t\tif (defaultBranch == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\tname: defaultBranch,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, number) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tnumber: number,\n\t\t}),\n\t})\n\tasync getIssueOrPullRequest(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tnumber: number,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<IssueOrPullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: {\n\t\t\t\t\tmergeRequest: GitLabMergeRequest | null;\n\t\t\t\t\tissue: GitLabIssue | null;\n\t\t\t\t} | null;\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getIssueOrMergeRequest(\n\t$fullPath: ID!\n\t$iid: String!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tmergeRequest(iid: $iid) {\n\t\t\tauthor {\n\t\t\t\tid\n\t\t\t\tname\n\t\t\t\tavatarUrl\n\t\t\t\twebUrl\n\t\t\t}\n\t\t\tiid\n\t\t\ttitle\n\t\t\tdescription\n\t\t\tstate\n\t\t\tcreatedAt\n\t\t\tupdatedAt\n\t\t\tmergedAt\n\t\t\twebUrl\n\t\t}\n\t\tissue(iid: $iid) {\n\t\t\tauthor {\n\t\t\t\tid\n\t\t\t\tname\n\t\t\t\tavatarUrl\n\t\t\t\twebUrl\n\t\t\t}\n\t\t\tiid\n\t\t\ttitle\n\t\t\tdescription\n\t\t\tstate\n\t\t\tcreatedAt\n\t\t\tupdatedAt\n\t\t\tclosedAt\n\t\t\twebUrl\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${owner}/${repo}`,\n\t\t\t\t\tiid: String(number),\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tif (rsp?.data?.project?.issue != null) {\n\t\t\t\tconst issue = rsp.data.project.issue;\n\t\t\t\treturn {\n\t\t\t\t\tprovider: provider,\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tid: issue.iid,\n\t\t\t\t\tnodeId: undefined,\n\t\t\t\t\tcreatedDate: new Date(issue.createdAt),\n\t\t\t\t\tupdatedDate: new Date(issue.updatedAt),\n\t\t\t\t\ttitle: issue.title,\n\t\t\t\t\tclosed: issue.state === 'closed',\n\t\t\t\t\tclosedDate: issue.closedAt == null ? undefined : new Date(issue.closedAt),\n\t\t\t\t\turl: issue.webUrl,\n\t\t\t\t\tstate: issue.state === 'locked' ? 'closed' : issue.state,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rsp?.data?.project?.mergeRequest != null) {\n\t\t\t\tconst mergeRequest = rsp.data.project.mergeRequest;\n\t\t\t\treturn {\n\t\t\t\t\tprovider: provider,\n\t\t\t\t\ttype: 'pullrequest',\n\t\t\t\t\tid: mergeRequest.iid,\n\t\t\t\t\tnodeId: undefined,\n\t\t\t\t\tcreatedDate: new Date(mergeRequest.createdAt),\n\t\t\t\t\tupdatedDate: new Date(mergeRequest.updatedAt),\n\t\t\t\t\ttitle: mergeRequest.title,\n\t\t\t\t\tclosed: mergeRequest.state === 'closed',\n\t\t\t\t\t// TODO@eamodio this isn't right, but GitLab doesn't seem to provide a closedAt on merge requests in GraphQL\n\t\t\t\t\tclosedDate: mergeRequest.state === 'closed' ? new Date(mergeRequest.updatedAt) : undefined,\n\t\t\t\t\turl: mergeRequest.webUrl,\n\t\t\t\t\tstate: mergeRequest.state === 'locked' ? 'closed' : mergeRequest.state,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * Whether `owner/repo` has issue `iid`. Strict like {@link getPullRequest}'s `strict`: `false` only for a `null`\n\t * project or issue, which is how GitLab's GraphQL answers a missing one; GraphQL `errors`, an empty response and\n\t * any HTTP error (a 404 means a wrong endpoint) all throw instead of reading as absent.\n\t */\n\t@trace({\n\t\targs: (provider, token, owner, repo, iid) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tiid: iid,\n\t\t}),\n\t})\n\tasync hasIssue(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tiid: number,\n\t\toptions: { baseUrl?: string; deferFailure?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<boolean> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: { project: { issue: { iid: string } | null } | null } | null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query hasIssue(\n\t$fullPath: ID!\n\t$iid: String!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tissue(iid: $iid) {\n\t\t\tiid\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{ fullPath: `${owner}/${repo}`, iid: String(iid) },\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions.deferFailure,\n\t\t\t);\n\t\t\tif (rsp?.data == null) throw new Error('GitLab returned no data for the issue');\n\n\t\t\treturn rsp.data.project?.issue != null;\n\t\t} catch (ex) {\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, branch) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tbranch: branch,\n\t\t}),\n\t})\n\tasync getPullRequestForBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tbranch: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t\tinclude?: GitLabMergeRequestState[];\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: {\n\t\t\t\t\tmergeRequests?: {\n\t\t\t\t\t\tnodes: GitLabMergeRequest[];\n\t\t\t\t\t} | null;\n\t\t\t\t\topened?: {\n\t\t\t\t\t\tnodes: GitLabMergeRequest[];\n\t\t\t\t\t} | null;\n\t\t\t\t\tclosed?: {\n\t\t\t\t\t\tnodes: GitLabMergeRequest[];\n\t\t\t\t\t} | null;\n\t\t\t\t\tmerged?: {\n\t\t\t\t\t\tnodes: GitLabMergeRequest[];\n\t\t\t\t\t} | null;\n\t\t\t\t} | null;\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst fragment = `\n\t\t\tnodes {\n\t\t\t\tiid\n\t\t\t\tauthor {\n\t\t\t\t\tid\n\t\t\t\t\tname\n\t\t\t\t\tavatarUrl\n\t\t\t\t\twebUrl\n\t\t\t\t}\n\t\t\t\ttitle\n\t\t\t\tdescription\n\t\t\t\tstate\n\t\t\t\tcreatedAt\n\t\t\t\tupdatedAt\n\t\t\t\tmergedAt\n\t\t\t\twebUrl\n\t\t\t}`;\n\n\t\t\tconst query = `query getMergeRequestForBranch(\n\t$fullPath: ID!\n\t$branches: [String!]\n) {\n\tproject(fullPath: $fullPath) {\n\t\t${\n\t\t\toptions?.include == null\n\t\t\t\t? `mergeRequests(sourceBranches: $branches sort: UPDATED_DESC first: 1) {\n\t\t\t${fragment}\n\t\t}`\n\t\t\t\t: ''\n\t\t}\n\t\t${\n\t\t\toptions?.include?.includes('opened')\n\t\t\t\t? `opened: mergeRequests(sourceBranches: $branches state: opened sort: UPDATED_DESC first: 1) {\n\t\t\t${fragment}\n\t\t}`\n\t\t\t\t: ''\n\t\t}\n\t\t${\n\t\t\toptions?.include?.includes('merged')\n\t\t\t\t? `merged: mergeRequests(sourceBranches: $branches state: merged sort: UPDATED_DESC first: 1) {\n\t\t\t${fragment}\n\t\t}`\n\t\t\t\t: ''\n\t\t}\n\t\t${\n\t\t\toptions?.include?.includes('closed')\n\t\t\t\t? `closed: mergeRequests(sourceBranches: $branches state: closed sort: UPDATED_DESC first: 1) {\n\t\t\t${fragment}\n\t\t}`\n\t\t\t\t: ''\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${owner}/${repo}`,\n\t\t\t\t\tbranches: [branch],\n\t\t\t\t\tstate: options?.include,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tlet pr: GitLabMergeRequest | undefined;\n\n\t\t\tif (options?.include == null) {\n\t\t\t\tpr = rsp?.data?.project?.mergeRequests?.nodes?.[0];\n\t\t\t} else {\n\t\t\t\tfor (const state of options.include) {\n\t\t\t\t\tlet mr;\n\t\t\t\t\tif (state === 'opened') {\n\t\t\t\t\t\tmr = rsp?.data?.project?.opened?.nodes?.[0];\n\t\t\t\t\t} else if (state === 'merged') {\n\t\t\t\t\t\tmr = rsp?.data?.project?.merged?.nodes?.[0];\n\t\t\t\t\t} else if (state === 'closed') {\n\t\t\t\t\t\tmr = rsp?.data?.project?.closed?.nodes?.[0];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (mr != null && (pr == null || new Date(mr.updatedAt) > new Date(pr.updatedAt))) {\n\t\t\t\t\t\tpr = mr;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (pr == null) return undefined;\n\n\t\t\treturn new PullRequest(\n\t\t\t\tprovider,\n\t\t\t\t{\n\t\t\t\t\tid: buildGitLabUserId(pr.author?.id) ?? '',\n\t\t\t\t\t// An absent author stays absent: no `'Unknown'` name a consumer can't tell from a real one, and no\n\t\t\t\t\t// `''` url/avatar that passes a presence check and renders as a link to nowhere. Matches\n\t\t\t\t\t// `fromProviderAccount`/`toIssueShape`.\n\t\t\t\t\tname: pr.author?.name ?? undefined,\n\t\t\t\t\tavatarUrl: pr.author?.avatarUrl ?? undefined,\n\t\t\t\t\turl: pr.author?.webUrl ?? undefined,\n\t\t\t\t},\n\t\t\t\t// oxlint-disable-next-line typescript/no-unnecessary-type-conversion\n\t\t\t\tString(pr.iid),\n\t\t\t\tundefined,\n\t\t\t\tpr.title,\n\t\t\t\tpr.webUrl,\n\t\t\t\t{ owner: owner, repo: repo },\n\t\t\t\tfromGitLabMergeRequestState(pr.state),\n\t\t\t\tnew Date(pr.createdAt),\n\t\t\t\tnew Date(pr.updatedAt),\n\t\t\t\t// TODO@eamodio this isn't right, but GitLab doesn't seem to provide a closedAt on merge requests in GraphQL\n\t\t\t\tpr.state !== 'closed' ? undefined : new Date(pr.updatedAt),\n\t\t\t\tpr.mergedAt == null ? undefined : new Date(pr.mergedAt),\n\t\t\t\tundefined, // mergeableState\n\t\t\t\tundefined, // viewerCanUpdate\n\t\t\t\tundefined, // refs\n\t\t\t\tundefined, // isDraft\n\t\t\t\tundefined, // additions\n\t\t\t\tundefined, // deletions\n\t\t\t\tundefined, // commentsCount\n\t\t\t\tundefined, // thumbsUpCount\n\t\t\t\tundefined, // reviewDecision\n\t\t\t\tundefined, // reviewRequests\n\t\t\t\tundefined, // latestReviews\n\t\t\t\tundefined, // assignees\n\t\t\t\tundefined, // statusCheckRollupState\n\t\t\t\tundefined, // project\n\t\t\t\tundefined, // version\n\t\t\t\tundefined, // commitCount\n\t\t\t\tundefined, // stack\n\t\t\t\tundefined, // filesChanged\n\t\t\t\tundefined, // body\n\t\t\t\tundefined, // number\n\t\t\t\tundefined, // authoredByMe\n\t\t\t\t'point',\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, rev) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\trev: rev,\n\t\t}),\n\t})\n\tasync getPullRequestForCommit(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\trev: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst projectId = await this.getProjectId(provider, token, owner, repo, options?.baseUrl, cancellation);\n\t\tif (!projectId) return undefined;\n\n\t\ttry {\n\t\t\tconst mrs = await this.request<GitLabMergeRequestREST[]>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`v4/projects/${projectId}/repository/commits/${rev}/merge_requests`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t// ...options,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tif (mrs == null || mrs.length === 0) return undefined;\n\n\t\t\tif (mrs.length > 1) {\n\t\t\t\tmrs.sort(\n\t\t\t\t\t(a, b) =>\n\t\t\t\t\t\t(a.state === 'opened' ? -1 : 1) - (b.state === 'opened' ? -1 : 1) ||\n\t\t\t\t\t\tnew Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn fromGitLabMergeRequestREST(mrs[0], provider, { owner: owner, repo: repo }, 'point');\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo, id) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tid: id,\n\t\t}),\n\t})\n\tasync getPullRequest(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tid: number,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\t/**\n\t\t\t * Returns `undefined` only for a `null` project or merge request, which is how GitLab's GraphQL\n\t\t\t * answers a missing one. A thrown not-found or an empty response means the endpoint is wrong, so\n\t\t\t * both throw instead of reading as absent.\n\t\t\t */\n\t\t\tstrict?: boolean;\n\t\t\tdeferFailure?: boolean;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: {\n\t\t\t\t\tmergeRequest: GitLabMergeRequestFull | null;\n\t\t\t\t} | null;\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getMergeRequest(\n\t$fullPath: ID!\n\t$iid: String!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tmergeRequest(iid: $iid) {\n\t\t\tid,\n\t\t\tiid\n\t\t\tstate,\n\t\t\tauthor {\n\t\t\t\tid\n\t\t\t\tname\n\t\t\t\tavatarUrl\n\t\t\t\twebUrl\n\t\t\t}\n\t\t\tdiffRefs {\n\t\t\t\tbaseSha\n\t\t\t\theadSha\n\t\t\t}\n\t\t\ttitle\n\t\t\tdescription\n\t\t\twebUrl\n\t\t\tcreatedAt\n\t\t\tupdatedAt\n\t\t\tmergedAt\n\t\t\ttargetBranch\n\t\t\tsourceBranch\n\t\t\tproject {\n\t\t\t\tid\n\t\t\t\tfullPath\n\t\t\t\twebUrl\n\t\t\t}\n\t\t\tsourceProject {\n\t\t\t\tid\n\t\t\t\tfullPath\n\t\t\t\twebUrl\n\t\t\t}\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${owner}/${repo}`,\n\t\t\t\t\tiid: String(id),\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions?.deferFailure,\n\t\t\t);\n\n\t\t\tif (rsp?.data == null) {\n\t\t\t\tif (options?.strict) throw new Error('GitLab returned no data for the merge request');\n\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tif (rsp.data.project?.mergeRequest == null) return undefined;\n\n\t\t\tconst pr = rsp.data.project.mergeRequest;\n\t\t\treturn fromGitLabMergeRequest(pr, provider, 'point');\n\t\t} catch (ex) {\n\t\t\tif (!options?.strict && ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * The iids of the merge requests into `owner/repo` whose source is `branch`, in any state, newest first, at most\n\t * `limit`: from the project itself when `headOwner` is omitted, otherwise from a fork in the `headOwner`\n\t * namespace. Only the iids, so a caller resolves the rows through the same read as its by-number lookups.\n\t *\n\t * Keyed on the source branch NAME, so a merged request whose branch was deleted still matches. Strict like\n\t * {@link getPullRequest}'s `strict`: an empty list means GitLab answered, a `null` project included; a 404 or\n\t * an empty response means the endpoint is wrong and throws. Requests from a deleted fork carry no source\n\t * project and so match nothing.\n\t */\n\t@trace({\n\t\targs: (provider, token, owner, repo, branch) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t\tbranch: branch,\n\t\t}),\n\t})\n\tasync getPullRequestNumbersForBranch(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\tbranch: string,\n\t\toptions: { baseUrl?: string; headOwner?: string; limit: number; deferFailure?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<{ numbers: number[]; truncated: boolean }> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface MergeRequestHead {\n\t\t\tiid: string;\n\t\t\tupdatedAt: string;\n\t\t\tsourceBranch: string;\n\t\t\tproject: { id: string };\n\t\t\tsourceProject: { id: string; fullPath: string } | null;\n\t\t}\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: {\n\t\t\t\t\tmergeRequests: {\n\t\t\t\t\t\tcount: number;\n\t\t\t\t\t\tpageInfo: { hasNextPage: boolean };\n\t\t\t\t\t\tnodes: MergeRequestHead[];\n\t\t\t\t\t} | null;\n\t\t\t\t} | null;\n\t\t\t} | null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getMergeRequestsForBranch(\n\t$fullPath: ID!\n\t$branches: [String!]\n\t$limit: Int!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tmergeRequests(sourceBranches: $branches, state: all, sort: UPDATED_DESC, first: $limit) {\n\t\t\tcount\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tiid\n\t\t\t\tupdatedAt\n\t\t\t\tsourceBranch\n\t\t\t\tproject {\n\t\t\t\t\tid\n\t\t\t\t}\n\t\t\t\tsourceProject {\n\t\t\t\t\tid\n\t\t\t\t\tfullPath\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}`;\n\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{ fullPath: `${owner}/${repo}`, branches: [branch], limit: options.limit },\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions.deferFailure,\n\t\t\t);\n\t\t\tif (rsp?.data == null) throw new Error('GitLab returned no data for the merge requests by branch');\n\n\t\t\tconst project = rsp.data.project;\n\t\t\tif (project == null) return { numbers: [], truncated: false };\n\n\t\t\tconst connection = project.mergeRequests;\n\t\t\tif (connection == null) {\n\t\t\t\tthrow new Error(`GitLab returned no merge requests for ${owner}/${repo}:${branch}`);\n\t\t\t}\n\n\t\t\tconst headOwner = options.headOwner;\n\t\t\tconst { values, truncated } = selectBranchPullRequests(connection.nodes, {\n\t\t\t\tmatchesHead: mr => {\n\t\t\t\t\tif (mr.sourceBranch !== branch || mr.sourceProject == null) return false;\n\t\t\t\t\tif (headOwner == null) return mr.sourceProject.id === mr.project.id;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\tmr.sourceProject.id !== mr.project.id &&\n\t\t\t\t\t\tequalsIgnoreCase(getRepoNamespace(mr.sourceProject.fullPath), headOwner)\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tupdatedAt: mr => Date.parse(mr.updatedAt),\n\t\t\t\tmap: mr => {\n\t\t\t\t\tconst iid = Number(mr.iid);\n\t\t\t\t\tif (!Number.isSafeInteger(iid)) throw new Error(`GitLab returned a merge request iid '${mr.iid}'`);\n\n\t\t\t\t\treturn iid;\n\t\t\t\t},\n\t\t\t\tlimit: options.limit,\n\t\t\t\tmore: connection.pageInfo.hasNextPage || connection.count > connection.nodes.length,\n\t\t\t});\n\t\t\treturn { numbers: values, truncated: truncated };\n\t\t} catch (ex) {\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * The change state of merge requests `iids` in `fullPath` — the cheap check behind the batch pull request read's\n\t * etags. Selects only what provider-apis' `getPullRequestForRepo` maps into a full row's etag inputs, plus the\n\t * fields of each of `options.etagIncludes`, in ONE request for up to {@link gitLabEtagFieldsMaxIids} iids. One\n\t * entry per input iid, in order; `undefined` is a PROVEN ABSENCE (see {@link getEtagFieldsByIid}).\n\t */\n\t@trace({\n\t\targs: (provider, token, fullPath, iids) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tfullPath: fullPath,\n\t\t\tiids: iids.length,\n\t\t}),\n\t})\n\tasync getMergeRequestsEtagFields(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tfullPath: string,\n\t\tiids: readonly number[],\n\t\toptions: { baseUrl?: string; etagIncludes: readonly PullRequestEtagInclude[]; deferFailure?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<(GitLabMergeRequestEtagNode | undefined)[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst selections = ['iid', 'state', 'draft', 'updatedAt', 'diffRefs { headSha }'];\n\t\tif (options.etagIncludes.includes('mergeable')) {\n\t\t\tselections.push('mergeStatusEnum');\n\t\t}\n\t\tif (options.etagIncludes.includes('reviewDecision')) {\n\t\t\tselections.push('reviewers { nodes { mergeRequestInteraction { reviewState } } }');\n\t\t}\n\t\tif (options.etagIncludes.includes('checks')) {\n\t\t\tselections.push('headPipeline { stages { nodes { jobs { nodes { status allowFailure } } } } }');\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getMergeRequestsEtagFields(\n\t$fullPath: ID!\n\t$iids: [String!]\n\t$first: Int!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tmergeRequests(iids: $iids, state: all, first: $first) {\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\t${selections.join('\\n\\t\\t\\t\\t')}\n\t\t\t}\n\t\t}\n\t}\n}`;\n\n\t\t\treturn await this.getEtagFieldsByIid<GitLabMergeRequestEtagNode>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t'mergeRequests',\n\t\t\t\tfullPath,\n\t\t\t\tiids,\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions.deferFailure,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * The change state of issues `iids` in `fullPath` — the cheap check behind the batch issue read's etags — selecting\n\t * only what provider-apis' issue read maps into a full row's etag inputs, plus the field of each of\n\t * `options.etagIncludes`, in ONE request for up to {@link gitLabEtagFieldsMaxIids} iids. One entry per input iid,\n\t * in order; `undefined` is a PROVEN ABSENCE (see {@link getEtagFieldsByIid}).\n\t */\n\t@trace({\n\t\targs: (provider, token, fullPath, iids) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\tfullPath: fullPath,\n\t\t\tiids: iids.length,\n\t\t}),\n\t})\n\tasync getIssuesEtagFields(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tfullPath: string,\n\t\tiids: readonly number[],\n\t\toptions: { baseUrl?: string; etagIncludes: readonly IssueEtagInclude[]; deferFailure?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<(GitLabIssueEtagNode | undefined)[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst selections = ['iid', 'closedAt', 'updatedAt'];\n\t\tif (options.etagIncludes.includes('reactions')) {\n\t\t\tselections.push('upvotes');\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getIssuesEtagFields(\n\t$fullPath: ID!\n\t$iids: [String!]\n\t$first: Int!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tissues(iids: $iids, state: all, first: $first) {\n\t\t\tpageInfo {\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\t${selections.join('\\n\\t\\t\\t\\t')}\n\t\t\t}\n\t\t}\n\t}\n}`;\n\n\t\t\treturn await this.getEtagFieldsByIid<GitLabIssueEtagNode>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t'issues',\n\t\t\t\tfullPath,\n\t\t\t\tiids,\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t\toptions.deferFailure,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t/**\n\t * Runs an etag read's `query` and maps its `connection`'s nodes back to `iids`, positionally.\n\t *\n\t * Strict like {@link getPullRequest}'s `strict`: an iid is absent only when the project is `null`, or when it is\n\t * missing from a complete page of an error-free reply — the answers that prove a miss to the full read's own\n\t * confirming read. GraphQL `errors`, an empty response, a `null` connection, a page that has more and any HTTP\n\t * error (a 404 means a wrong endpoint) throw instead. The query asks for `state: all`, so no default state filter\n\t * can hide a closed or merged item and have it read as missing.\n\t */\n\tprivate async getEtagFieldsByIid<T extends { iid: string }>(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string | undefined,\n\t\tquery: string,\n\t\tconnection: 'mergeRequests' | 'issues',\n\t\tfullPath: string,\n\t\tiids: readonly number[],\n\t\tcancellation: AbortSignal | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tdeferFailure: boolean | undefined,\n\t): Promise<(T | undefined)[]> {\n\t\tif (!iids.length) return [];\n\t\tif (iids.length > gitLabEtagFieldsMaxIids) {\n\t\t\tthrow new Error(`Cannot read more than ${gitLabEtagFieldsMaxIids} GitLab iids in one request`);\n\t\t}\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tproject: Partial<\n\t\t\t\t\tRecord<'mergeRequests' | 'issues', { pageInfo: { hasNextPage: boolean }; nodes: T[] } | null>\n\t\t\t\t> | null;\n\t\t\t} | null;\n\t\t}\n\n\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\tprovider,\n\t\t\ttoken,\n\t\t\tbaseUrl,\n\t\t\tquery,\n\t\t\t{ fullPath: fullPath, iids: iids.map(String), first: iids.length },\n\t\t\tcancellation,\n\t\t\tscope,\n\t\t\tdeferFailure,\n\t\t);\n\t\tif (rsp?.data == null) throw new Error(`GitLab returned no data for the ${connection} of ${fullPath}`);\n\n\t\tconst project = rsp.data.project;\n\t\tif (project == null) return iids.map(() => undefined);\n\n\t\tconst page = project[connection];\n\t\tif (page?.nodes == null) throw new Error(`GitLab returned no ${connection} for ${fullPath}`);\n\t\tif (page.pageInfo.hasNextPage) throw new Error(`GitLab returned a partial page of ${connection}`);\n\n\t\tconst byIid = new Map(page.nodes.map(node => [node.iid, node]));\n\t\treturn iids.map(iid => byIid.get(String(iid)));\n\t}\n\n\t@trace({\n\t\targs: (provider, token, owner, repo) => ({\n\t\t\tprovider: provider.name,\n\t\t\ttoken: `<token:${token.microHash}>`,\n\t\t\towner: owner,\n\t\t\trepo: repo,\n\t\t}),\n\t})\n\tasync getRepositoryMetadata(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\towner: string,\n\t\trepo: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<RepositoryMetadata | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst projectId = await this.getProjectId(provider, token, owner, repo, options?.baseUrl, cancellation);\n\t\tif (!projectId) return undefined;\n\n\t\ttry {\n\t\t\tconst proj = await this.request<GitLabProjectREST>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\t`v4/projects/${projectId}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t// ...options,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tif (proj == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tprovider: provider,\n\t\t\t\towner: proj.namespace.full_path,\n\t\t\t\tname: proj.path,\n\t\t\t\tisFork: proj.forked_from_project != null,\n\t\t\t\tparent:\n\t\t\t\t\tproj.forked_from_project != null\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\towner: proj.forked_from_project.namespace.full_path,\n\t\t\t\t\t\t\t\tname: proj.forked_from_project.path,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t} satisfies RepositoryMetadata;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\t@trace({ args: (provider, token) => ({ provider: provider.name, token: `<token:${token.microHash}>` }) })\n\tasync searchPullRequests(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\toptions?: {\n\t\t\tsearch?: string;\n\t\t\tuser?: string;\n\t\t\trepos?: string[];\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t\tinclude?: PullRequestState[];\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<PullRequest[]> {\n\t\tconst scope = getScopedLogger();\n\t\tconst search = options?.search;\n\t\tif (!search) {\n\t\t\treturn [];\n\t\t}\n\n\t\ttry {\n\t\t\tconst perPageLimit = 20; // with bigger amount we exceed the max GraphQL complexity in the next query\n\t\t\tconst include = options?.include?.length ? options.include : undefined;\n\t\t\t// GitLab's search API takes a single `state` value, so push it server-side when exactly one state is\n\t\t\t// requested; that keeps the requested state from being crowded out of the `perPageLimit` slice. For\n\t\t\t// multiple states there's no single-value qualifier, so those are filtered client-side below.\n\t\t\tlet searchPath = `v4/search/?scope=merge_requests&search=${encodeURIComponent(search)}&per_page=${perPageLimit}`;\n\t\t\tif (include?.length === 1) {\n\t\t\t\tsearchPath += `&state=${encodeURIComponent(toGitLabMergeRequestState(include[0]))}`;\n\t\t\t}\n\n\t\t\tlet restPRs: GitLabMergeRequestREST[];\n\t\t\tif (include == null) {\n\t\t\t\t// Unfiltered search returns the first page as-is (as it did before state filtering existed).\n\t\t\t\trestPRs = await this.request<GitLabMergeRequestREST[]>(\n\t\t\t\t\tprovider,\n\t\t\t\t\ttoken,\n\t\t\t\t\toptions?.baseUrl,\n\t\t\t\t\tsearchPath,\n\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\tcancellation,\n\t\t\t\t\tscope,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// The search API returns MRs of all states in relevance order, so the requested state(s) may not\n\t\t\t\t// appear on the first page. Server-side `&state=` narrows the single-state case when honored, but it\n\t\t\t\t// can't express multiple states, so page through the results and accumulate matches until we fill the\n\t\t\t\t// detail-query cap (`perPageLimit`) or exhaust the search results. This keeps correctness whether\n\t\t\t\t// or not `&state=` takes effect. Bounded by `maxSearchPages` like the other paged provider drains in\n\t\t\t\t// this package so a large, low-match result set can't fan out into an unbounded request loop.\n\t\t\t\tconst maxSearchPages = 20;\n\t\t\t\tconst matches: GitLabMergeRequestREST[] = [];\n\t\t\t\tfor (let page = 1; page <= maxSearchPages; page++) {\n\t\t\t\t\tconst pagePRs = await this.request<GitLabMergeRequestREST[]>(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\ttoken,\n\t\t\t\t\t\toptions?.baseUrl,\n\t\t\t\t\t\t`${searchPath}&page=${page}`,\n\t\t\t\t\t\t{ method: 'GET' },\n\t\t\t\t\t\tcancellation,\n\t\t\t\t\t\tscope,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const pr of pagePRs) {\n\t\t\t\t\t\tif (include.includes(fromGitLabMergeRequestState(pr.state))) {\n\t\t\t\t\t\t\tmatches.push(pr);\n\t\t\t\t\t\t\tif (matches.length >= perPageLimit) break;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Stop once we've filled the cap or hit the last (partial or empty) page.\n\t\t\t\t\tif (matches.length >= perPageLimit || pagePRs.length < perPageLimit) break;\n\t\t\t\t}\n\t\t\t\trestPRs = matches;\n\t\t\t}\n\t\t\tif (restPRs.length === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tinterface QueryResult {\n\t\t\t\tdata: Record<`mergeRequest_${number}`, GitLabMergeRequestFull>;\n\t\t\t}\n\n\t\t\tconst queryArgs = restPRs.map((_, i) => `$id_${i}: MergeRequestID!`).join('\\n');\n\t\t\tconst queryFields = restPRs\n\t\t\t\t.map((_, i) => `mergeRequest_${i}: mergeRequest(id: $id_${i}) { ...mergeRequestFields }`)\n\t\t\t\t.join('\\n');\n\t\t\t// Set of fields includes only additional fields that are not included in GitLabMergeRequestREST.\n\t\t\t// It's limited as much as possible to reduce complexity of the query.\n\t\t\tconst queryMrFields = `fragment mergeRequestFields on MergeRequest {\n\t\t\t\tdiffRefs {\n\t\t\t\t\tbaseSha\n\t\t\t\t\theadSha\n\t\t\t\t}\n\t\t\t\tproject {\n\t\t\t\t\tid\n\t\t\t\t\tfullPath\n\t\t\t\t\twebUrl\n\t\t\t\t}\n\t\t\t\tsourceProject {\n\t\t\t\t\tid\n\t\t\t\t\tfullPath\n\t\t\t\t\twebUrl\n\t\t\t\t}\n\t\t\t}`;\n\t\t\tconst query = `query getMergeRequests (${queryArgs}) {${queryFields}} ${queryMrFields}`;\n\n\t\t\tconst params = restPRs.reduce<Record<`id_${number}`, string>>((ids, gitlabRestPr, i) => {\n\t\t\t\tids[`id_${i}`] = `${gitlabMergeRequestIdPrefix}${gitlabRestPr.id}`;\n\t\t\t\treturn ids;\n\t\t\t}, {});\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\tparams,\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\t\t\tif (rsp?.data != null) {\n\t\t\t\tconst resultPRs = restPRs.reduce<PullRequest[]>((accum, restPR, i) => {\n\t\t\t\t\tconst graphQlPR = rsp.data[`mergeRequest_${i}`];\n\t\t\t\t\tif (graphQlPR == null) {\n\t\t\t\t\t\treturn accum;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fullPr: GitLabMergeRequestFull = {\n\t\t\t\t\t\t...graphQlPR,\n\t\t\t\t\t\tiid: String(restPR.iid),\n\t\t\t\t\t\tid: String(restPR.id),\n\t\t\t\t\t\tstate: restPR.state,\n\t\t\t\t\t\t// An absent REST author stays absent (`null`, as GitLab's own shape expresses it) rather than a\n\t\t\t\t\t\t// synthesized `'Unknown'` stub: `fromGitLabMergeRequest` collapses `null` to an absent\n\t\t\t\t\t\t// `PullRequestMember.name`, whereas a placeholder here would launder into a real-looking name.\n\t\t\t\t\t\tauthor:\n\t\t\t\t\t\t\trestPR.author != null\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\tid: buildGitLabUserId(restPR.author.id) ?? '',\n\t\t\t\t\t\t\t\t\t\tname: restPR.author.name,\n\t\t\t\t\t\t\t\t\t\tavatarUrl: restPR.author.avatar_url ?? null,\n\t\t\t\t\t\t\t\t\t\twebUrl: restPR.author.web_url,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\ttitle: restPR.title,\n\t\t\t\t\t\tdescription: restPR.description,\n\t\t\t\t\t\twebUrl: restPR.web_url,\n\t\t\t\t\t\tcreatedAt: restPR.created_at,\n\t\t\t\t\t\tupdatedAt: restPR.updated_at,\n\t\t\t\t\t\tmergedAt: restPR.merged_at,\n\t\t\t\t\t\tsourceBranch: restPR.source_branch,\n\t\t\t\t\t\ttargetBranch: restPR.target_branch,\n\t\t\t\t\t};\n\t\t\t\t\taccum.push(fromGitLabMergeRequest(fullPr, provider, 'text-search'));\n\t\t\t\t\treturn accum;\n\t\t\t\t}, []);\n\t\t\t\treturn resultPRs;\n\t\t\t}\n\t\t\treturn [];\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return [];\n\n\t\t\tthrow this.handleException(ex, provider, scope);\n\t\t}\n\t}\n\n\tprivate async findUser(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tsearch: string,\n\t\toptions?: {\n\t\t\tbaseUrl?: string;\n\t\t\tavatarSize?: number;\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitLabUser[]> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: {\n\t\t\t\tusers: {\n\t\t\t\t\tnodes: {\n\t\t\t\t\t\tid: string;\n\t\t\t\t\t\tname: string;\n\t\t\t\t\t\tusername: string;\n\t\t\t\t\t\tpublicEmail?: string;\n\t\t\t\t\t\tstate: string;\n\t\t\t\t\t\tavatarUrl: string;\n\t\t\t\t\t\twebUrl: string;\n\t\t\t\t\t}[];\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query findUser(\n$search: String!\n) {\n\tusers(search: $search) {\n\t\tnodes {\n\t\t\tid\n\t\t\tname\n\t\t\tusername,\n\t\t\tpublicEmail,\n\t\t\tstate\n\t\t\tavatarUrl\n\t\t\twebUrl\n\t\t}\n\t}\n}`;\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\toptions?.baseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tsearch: search,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tconst matches = rsp?.data?.users?.nodes;\n\t\t\tif (matches == null || matches.length === 0) return [];\n\n\t\t\tconst users: GitLabUser[] = [];\n\n\t\t\tfor (const user of matches) {\n\t\t\t\tconst match = /gid:\\/\\/gitlab\\/User\\/([0-9]+)\\b/.exec(user.id);\n\t\t\t\tif (match == null) continue;\n\n\t\t\t\tusers.push({\n\t\t\t\t\tid: parseInt(match[1], 10),\n\t\t\t\t\tname: user.name,\n\t\t\t\t\tusername: user.username,\n\t\t\t\t\tpublicEmail: user.publicEmail || undefined,\n\t\t\t\t\tstate: user.state,\n\t\t\t\t\tavatarUrl: user.avatarUrl,\n\t\t\t\t\twebUrl: user.webUrl,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return [];\n\n\t\t\tthis.handleException(ex, provider, scope);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tgetProjectId(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tgroup: string,\n\t\trepo: string,\n\t\tbaseUrl: string | undefined,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<string | undefined> {\n\t\tconst { accessToken } = token;\n\t\tconst key = `${accessToken}|${group}/${repo}`;\n\n\t\tlet projectId = this._projectIds.get(key);\n\t\tif (projectId == null) {\n\t\t\tprojectId = this.getProjectIdCore(provider, token, group, repo, baseUrl, cancellation);\n\t\t\tthis._projectIds.set(key, projectId);\n\t\t}\n\n\t\treturn projectId;\n\t}\n\n\tprivate async getProjectIdCore(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tgroup: string,\n\t\trepo: string,\n\t\tbaseUrl: string | undefined,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<string | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tinterface QueryResult {\n\t\t\tdata: { project: { id: string } };\n\t\t}\n\n\t\ttry {\n\t\t\tconst query = `query getProjectId(\n\t$fullPath: ID!\n) {\n\tproject(fullPath: $fullPath) {\n\t\tid\n\t}\n}`;\n\t\t\tconst rsp = await this.graphql<QueryResult>(\n\t\t\t\tprovider,\n\t\t\t\ttoken,\n\t\t\t\tbaseUrl,\n\t\t\t\tquery,\n\t\t\t\t{\n\t\t\t\t\tfullPath: `${group}/${repo}`,\n\t\t\t\t},\n\t\t\t\tcancellation,\n\t\t\t\tscope,\n\t\t\t);\n\n\t\t\tconst gid = rsp?.data?.project?.id;\n\t\t\tif (gid == null) return undefined;\n\n\t\t\tconst match = /gid:\\/\\/gitlab\\/Project\\/([0-9]+)\\b/.exec(gid);\n\t\t\tif (match == null) return undefined;\n\n\t\t\tconst projectId = match[1];\n\n\t\t\tscope?.addExitInfo(`projectId=${projectId}`);\n\t\t\treturn projectId;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof RequestNotFoundError) return undefined;\n\n\t\t\tthis.handleException(ex, provider, scope);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async graphql<T extends object>(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string | undefined,\n\t\tquery: string,\n\t\tvariables: Record<string, any>,\n\t\tcancellation: AbortSignal | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t\tdeferFailure?: boolean,\n\t): Promise<T | undefined> {\n\t\tconst { accessToken } = token;\n\t\tlet rsp: Response;\n\t\ttry {\n\t\t\tconst sw = maybeStopWatch(`[GITLAB] POST ${baseUrl}`, { log: { onlyExit: true } });\n\n\t\t\ttry {\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\trsp = await this.config.wrapForForcedInsecureSSL(provider.getIgnoreSSLErrors(), () =>\n\t\t\t\t\tthis.config.fetch(`${baseUrl ?? 'https://gitlab.com/api'}/graphql`, {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n\t\t\t\t\t\tsignal: cancellation,\n\t\t\t\t\t\tbody: JSON.stringify({ query: query, variables: variables }),\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\tif (rsp.ok) {\n\t\t\t\t\tconst data = (await rsp.json()) as T | { errors: { message: string }[] };\n\n\t\t\t\t\tif ('errors' in data) throw new ProviderFetchError('GitLab', rsp, data.errors);\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// Reads the body so the 403 branch below can tell a throttled request from a permission failure.\n\t\t\t\tthrow await ProviderFetchError.fromResponse('GitLab', rsp);\n\t\t\t} finally {\n\t\t\t\tconst match = /(^[^({\\n]+)/.exec(query);\n\t\t\t\tconst message = ` ${match?.[1].trim() ?? query}`;\n\n\t\t\t\tsw?.stop({ message: message });\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof ProviderFetchError || ex.name === 'AbortError') {\n\t\t\t\tthis.handleRequestError(provider, token, ex, scope, deferFailure);\n\t\t\t} else if (Logger.isDebugging) {\n\t\t\t\tthis.config.onError?.(`GitLab request failed: ${ex.message}`);\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate async request<T>(\n\t\tprovider: Provider,\n\t\ttoken: TokenWithInfo,\n\t\tbaseUrl: string | undefined,\n\t\troute: string,\n\t\toptions: { method: RequestInit['method'] } & Record<string, unknown>,\n\t\tcancellation: AbortSignal | undefined,\n\t\tscope: ScopedLogger | undefined,\n\t): Promise<T> {\n\t\tconst { accessToken } = token;\n\t\tconst url = `${baseUrl ?? 'https://gitlab.com/api'}/${route}`;\n\n\t\tlet rsp: Response;\n\t\ttry {\n\t\t\tconst sw = maybeStopWatch(`[GITLAB] ${options?.method ?? 'GET'} ${url}`, { log: { onlyExit: true } });\n\n\t\t\ttry {\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\t\t\trsp = await this.config.wrapForForcedInsecureSSL(provider.getIgnoreSSLErrors(), () =>\n\t\t\t\t\tthis.config.fetch(url, {\n\t\t\t\t\t\theaders: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },\n\t\t\t\t\t\tsignal: cancellation,\n\t\t\t\t\t\t...options,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\tif (rsp.ok) {\n\t\t\t\t\treturn (await rsp.json()) as T;\n\t\t\t\t}\n\n\t\t\t\t// Reads the body so the 403 branch below can tell a throttled request from a permission failure.\n\t\t\t\tthrow await ProviderFetchError.fromResponse('GitLab', rsp);\n\t\t\t} finally {\n\t\t\t\tsw?.stop();\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof ProviderFetchError || ex.name === 'AbortError') {\n\t\t\t\tthis.handleRequestError(provider, token, ex, scope, undefined);\n\t\t\t} else if (Logger.isDebugging) {\n\t\t\t\tthis.config.onError?.(`GitLab request failed: ${ex.message}`);\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate handleRequestError(\n\t\tprovider: Provider | undefined,\n\t\ttoken: TokenWithInfo,\n\t\tex: ProviderFetchError | (Error & { name: 'AbortError' }),\n\t\tscope: ScopedLogger | undefined,\n\t\tdeferFailure: boolean | undefined,\n\t): void {\n\t\tif (ex.name === 'AbortError' || !(ex instanceof ProviderFetchError)) throw new CancellationError(ex);\n\n\t\tconst { accessToken, ...tokenInfo } = token;\n\t\tswitch (ex.status) {\n\t\t\tcase 404: // Not found\n\t\t\tcase 410: // Gone\n\t\t\tcase 422: // Unprocessable Entity\n\t\t\t\tthrow new RequestNotFoundError(ex);\n\t\t\tcase 429: // Too Many Requests\n\t\t\t\tthrow toRateLimitError(ex, accessToken);\n\t\t\tcase 401: // Unauthorized\n\t\t\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Unauthorized, ex);\n\t\t\tcase 403: // Forbidden\n\t\t\t\t// GitLab returns 403 for both a permission failure and a throttled request, so the message is the\n\t\t\t\t// discriminant (see `isRateLimitResponse`, shared with Bitbucket/Azure).\n\t\t\t\tif (isRateLimitResponse(ex)) throw toRateLimitError(ex, accessToken);\n\n\t\t\t\tthrow new AuthenticationError(tokenInfo, AuthenticationErrorReason.Forbidden, ex);\n\t\t\tcase 500: // Internal Server Error\n\t\t\t\tscope?.error(ex);\n\t\t\t\tif (ex.response != null) {\n\t\t\t\t\treportRequestFailure(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\tex,\n\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\tthis.config.onRequestFailed?.(\n\t\t\t\t\t\t\t\tprovider == null || provider.id === 'gitlab'\n\t\t\t\t\t\t\t\t\t? l10n.t(\n\t\t\t\t\t\t\t\t\t\t\t'{0} failed to respond and might be experiencing issues. Please visit the [GitLab status page](https://status.gitlab.com) for more information.',\n\t\t\t\t\t\t\t\t\t\t\tprovider?.name ?? 'GitLab',\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t: l10n.t('{0} failed to respond and might be experiencing issues.', provider.name),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\tdeferFailure,\n\t\t\t\t\t\ttoken.connectionId,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 502: // Bad Gateway\n\t\t\t\tscope?.error(ex);\n\t\t\t\t// GitHub seems to return this status code for timeouts\n\t\t\t\tif (ex.message.includes('timeout')) {\n\t\t\t\t\treportRequestFailure(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\tex,\n\t\t\t\t\t\t() => this.config.onRequestTimedOut?.(provider?.name ?? 'GitLab'),\n\t\t\t\t\t\tdeferFailure,\n\t\t\t\t\t\ttoken.connectionId,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (ex.status >= 400 && ex.status < 500) throw new RequestClientError(ex);\n\t\t\t\tbreak;\n\t\t}\n\n\t\tscope?.error(ex);\n\t\tif (Logger.isDebugging) {\n\t\t\tthis.config.onError?.(`GitLab request failed: ${(ex.response as any)?.errors?.[0]?.message ?? ex.message}`);\n\t\t}\n\t}\n\n\tprivate handleException(ex: Error, provider: Provider, scope: ScopedLogger | undefined): Error {\n\t\tscope?.error(ex);\n\t\t// debugger;\n\n\t\tif (ex instanceof AuthenticationError) {\n\t\t\tvoid this.showAuthenticationErrorMessage(ex, provider);\n\t\t}\n\t\treturn ex;\n\t}\n\n\tprivate async showAuthenticationErrorMessage(ex: AuthenticationError, provider: Provider) {\n\t\tif (ex.reason === AuthenticationErrorReason.Unauthorized || ex.reason === AuthenticationErrorReason.Forbidden) {\n\t\t\tconst reauthenticate = await this.config.onReauthenticationRequired?.(\n\t\t\t\tex.reason === AuthenticationErrorReason.Forbidden\n\t\t\t\t\t? l10n.t('{0}. Would you like to try reauthenticating to provide additional access?', ex.message)\n\t\t\t\t\t: l10n.t('{0}. Would you like to try reauthenticating?', ex.message),\n\t\t\t);\n\n\t\t\tif (reauthenticate) {\n\t\t\t\tawait provider.reauthenticate();\n\t\t\t\tthis.resetCaches();\n\t\t\t}\n\t\t} else {\n\t\t\tthis.config.onError?.(ex.message);\n\t\t}\n\t}\n}\n\n/** Wires a {@link GitLabApi} from the full runtime context, mapping `ctx` down to the narrow config. */\nexport function createGitLabApi(ctx: IntegrationServiceContext): GitLabApi {\n\tconst config: ProviderApiConfig = {\n\t\t...baseProviderApiConfig(ctx),\n\t\t// Self-hosted GitLab keys its API base off `remotes`, so reset on both HTTP proxy and remotes changes.\n\t\tonConfigChanged: listener =>\n\t\t\tctx.config.onDidChange(e => {\n\t\t\t\tif (e.httpProxy || e.remotes) {\n\t\t\t\t\tlistener();\n\t\t\t\t}\n\t\t\t}),\n\t\tonReauthenticationRequired: message =>\n\t\t\tctx.hooks?.onReauthenticationRequired?.(message) ?? Promise.resolve(false),\n\t\tonRequestTimedOut: providerName => ctx.hooks?.ui?.onRequestTimedOut?.(providerName),\n\t};\n\n\treturn new GitLabApi(config);\n}\n"],"mappings":"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"}
@@ -5,10 +5,10 @@ import "../../../git/models/pullRequest.js";
5
5
  import { IntegrationReadUnavailableError } from "../errors.js";
6
6
  import "../constants.js";
7
7
  import { toCollectionScopeFailure } from "../collectionMetadata.js";
8
- import { toTokenWithInfo } from "../authentication/models.js";
9
8
  import "../providerFilters.js";
10
9
  import { collectProviderPagedResult, mergeCollectionMetadata, readEtagFieldsByRepository, resolveBranchPullRequests } from "./utils/providerPaging.js";
11
10
  import { ProviderPullRequestReviewState, fromProviderPullRequest, getProviderPullRequestIdentity, providersMetadata, toIssueShape, toProviderPullRequestStates } from "./models.js";
11
+ import { toTokenWithInfo } from "../authentication/models.js";
12
12
  import { getGitLabPullRequestIdentityFromMaybeUrl, matchesGitLabOrgNamespace } from "./gitlab/gitlab.utils.js";
13
13
  import { fromGitLabMergeRequestProvidersApi, toGitLabIssueEtagFields, toGitLabPullRequestEtagFields } from "./gitlab/models.js";
14
14
  import { GitHostIntegration } from "../models/gitHostIntegration.js";
@@ -2,10 +2,10 @@ import { Logger as _logger } from "../../../utils/logger.js";
2
2
  import "../constants.js";
3
3
  import { isJiraServerMissingProjectError } from "./providerErrors.js";
4
4
  import { isIncompleteCollection, toCollectionScopeFailure } from "../collectionMetadata.js";
5
- import { toTokenWithInfo } from "../authentication/models.js";
6
- import { areDomainsOnSameHost, baseUrlFromDomain } from "../utils/domain.utils.js";
7
5
  import { mergeCollectionMetadata } from "./utils/providerPaging.js";
6
+ import { areDomainsOnSameHost, baseUrlFromDomain } from "../utils/domain.utils.js";
8
7
  import { providersMetadata, toAccount, toIssueShape, toProviderIssueStates } from "./models.js";
8
+ import { toTokenWithInfo } from "../authentication/models.js";
9
9
  import { IssuesIntegration, groupProjectIssuesSearches, toProjectsCollection } from "../models/issuesIntegration.js";
10
10
  import { createIssueAutolinkReferences } from "./utils/issueAutolinks.js";
11
11
  import { DiscoveryCache } from "./utils/discoveryCache.js";
@@ -4,9 +4,9 @@ import { mapSettledBounded } from "../../../utils/promise.js";
4
4
  import "../constants.js";
5
5
  import { isJiraMissingProjectError } from "./providerErrors.js";
6
6
  import { toCollectionScopeFailure } from "../collectionMetadata.js";
7
- import { toTokenWithInfo } from "../authentication/models.js";
8
7
  import { collectProviderPagedResult, mergeCollectionMetadata } from "./utils/providerPaging.js";
9
8
  import { providersMetadata, toAccount, toIssueShape, toProviderIssueStates } from "./models.js";
9
+ import { toTokenWithInfo } from "../authentication/models.js";
10
10
  import { IssuesIntegration, groupProjectIssuesSearches } from "../models/issuesIntegration.js";
11
11
  import { createIssueAutolinkReferences } from "./utils/issueAutolinks.js";
12
12
  import { DiscoveryCache } from "./utils/discoveryCache.js";
@@ -92,9 +92,9 @@ export declare class LinearIntegration extends IssuesIntegration<IssuesCloudHost
92
92
  protected getProviderIssuesForProject(session: ProviderAuthenticationSession, project: ResourceDescriptor, options?: IssuesForProjectOptions): Promise<IssueShape[] | undefined>;
93
93
  protected getProviderIssuesForProjectWithTruncation(session: ProviderAuthenticationSession, project: ResourceDescriptor, options?: IssuesForProjectOptions): Promise<ProjectIssuesDrain | undefined>;
94
94
  /**
95
- * Searches every team of a user-scoped read together: one `getIssues` names all of them and the viewer as
96
- * assignee, so a team with none of the user's issues costs no request of its own. Linear's filter is a GraphQL
97
- * body, not a query string, so there is no key bound to chunk at.
95
+ * Searches every team of a user-scoped read together: one `getIssues` names all of them and the viewer's scope
96
+ * (see {@link viewerScope}), so a team with none of the user's issues costs no request of its own. Linear's filter
97
+ * is a GraphQL body, not a query string, so there is no key bound to chunk at.
98
98
  *
99
99
  * A read narrowed to Linear Projects is searched together even across every assignee: the Projects already bound
100
100
  * it to a few teams' worth of issues, so a team holding none of them costs no request either. An unnarrowed read
@@ -114,7 +114,7 @@ export declare class LinearIntegration extends IssuesIntegration<IssuesCloudHost
114
114
  * Follows `getIssues`' cursor up to {@link maxPagesPerRequest}. `truncated` is set when that backstop stopped
115
115
  * the drain with more pages still available, when the provider stalled its cursor or flagged its paging as
116
116
  * truncated, or when a page after the first failed, which is recorded at `failureScope` while the prefix is
117
- * kept. A first-page failure throws, so the caller sees a hard error rather than an empty success.
117
+ * kept. A failure before any issue was kept throws, so the caller sees a hard error rather than an empty success.
118
118
  */
119
119
  private drainIssues;
120
120
  get id(): IssuesCloudHostIntegrationId.Linear;
@@ -9,9 +9,10 @@ import { isIssueResourceDescriptor } from "../../../git/utils/resourceDescriptor
9
9
  import { IntegrationReadUnavailableError, toError } from "../errors.js";
10
10
  import "../constants.js";
11
11
  import { toCollectionScopeFailure } from "../collectionMetadata.js";
12
- import { toTokenWithInfo } from "../authentication/models.js";
12
+ import "../providerFilters.js";
13
13
  import { collectProviderPagedResult, mergeCollectionMetadata } from "./utils/providerPaging.js";
14
14
  import { fromProviderIssue, providersMetadata, toIssueShape, toProviderIssueStates } from "./models.js";
15
+ import { toTokenWithInfo } from "../authentication/models.js";
15
16
  import { isReadSessionFailure } from "../models/integration.js";
16
17
  import { IssuesIntegration, groupProjectIssuesSearches, splitProjectIssuesSearch } from "../models/issuesIntegration.js";
17
18
  import { createIssueAutolinkReferences } from "./utils/issueAutolinks.js";
@@ -46,6 +47,20 @@ const maxWorkspaceProjectPages = 20;
46
47
  const recentAccountTTL = 6e4;
47
48
  /** A team key and issue number, as Linear writes an issue's identifier (`ENG-123`). */
48
49
  const linearIdentifier = /^([A-Za-z0-9]+)-(\d+)$/;
50
+ /**
51
+ * How a user-scoped team read narrows to the viewer: the issues assigned to them, plus, unless the caller asked for
52
+ * assigned issues by name, the GitHub-synced issues they created. Linear's GitHub Issues sync makes the GitHub author
53
+ * of an issue (one who linked their GitHub account) the creator of its Linear copy but assigns no one GitHub doesn't,
54
+ * so an assignee-only read never returns the copy of a GitHub issue the user filed (gitkraken/kepler#4480). Only
55
+ * synced issues widen, and only those the viewer created on either side: the rest of what the user created in Linear
56
+ * stays out of "my issues", as does a synced issue they merely follow.
57
+ */
58
+ function viewerScope(viewerId, filters) {
59
+ return filters?.includes("assignee") ? { assignees: [viewerId] } : {
60
+ assignees: [viewerId],
61
+ githubSyncedCreators: [viewerId]
62
+ };
63
+ }
49
64
  var LinearIntegration = class extends IssuesIntegration {
50
65
  /**
51
66
  * Built from the teams, so it follows them: rebuilt when the team list changes or the caches are dropped, and once
@@ -171,7 +186,7 @@ var LinearIntegration = class extends IssuesIntegration {
171
186
  try {
172
187
  const projects = await this.getWorkspaceProjects(session, options?.fresh === true);
173
188
  await this.confirmScopedAuthFailures(session, projects.metadata);
174
- this.resetRequestExceptionCount("getProjectsForResources");
189
+ this.resetRequestExceptionCount("getProjectsForResources", connectionId);
175
190
  return { value: projects };
176
191
  } catch (ex) {
177
192
  this.handleProviderException("getProjectsForResources", ex, { connectionId });
@@ -308,7 +323,7 @@ var LinearIntegration = class extends IssuesIntegration {
308
323
  const drain = await this.drainIssues(session, {
309
324
  teams: [project.id],
310
325
  ...options?.linearProjects?.length ? { projects: [...options.linearProjects] } : {},
311
- ...viewerId != null ? { assignees: [viewerId] } : {},
326
+ ...viewerId != null ? viewerScope(viewerId, options?.filters) : {},
312
327
  states: toProviderIssueStates(options?.state)
313
328
  }, options?.sort, {
314
329
  providerId: this.id,
@@ -326,9 +341,9 @@ var LinearIntegration = class extends IssuesIntegration {
326
341
  };
327
342
  }
328
343
  /**
329
- * Searches every team of a user-scoped read together: one `getIssues` names all of them and the viewer as
330
- * assignee, so a team with none of the user's issues costs no request of its own. Linear's filter is a GraphQL
331
- * body, not a query string, so there is no key bound to chunk at.
344
+ * Searches every team of a user-scoped read together: one `getIssues` names all of them and the viewer's scope
345
+ * (see {@link viewerScope}), so a team with none of the user's issues costs no request of its own. Linear's filter
346
+ * is a GraphQL body, not a query string, so there is no key bound to chunk at.
332
347
  *
333
348
  * A read narrowed to Linear Projects is searched together even across every assignee: the Projects already bound
334
349
  * it to a few teams' worth of issues, so a team holding none of them costs no request either. An unnarrowed read
@@ -341,6 +356,7 @@ var LinearIntegration = class extends IssuesIntegration {
341
356
  options.userId ?? "",
342
357
  options.sort ?? "",
343
358
  options.state ?? "",
359
+ options.filters?.join(",") ?? "",
344
360
  ...options.linearProjects ?? []
345
361
  ].join("\0") : void 0);
346
362
  }
@@ -356,7 +372,7 @@ var LinearIntegration = class extends IssuesIntegration {
356
372
  const drain = await this.drainIssues(session, {
357
373
  teams: teams.map((team) => team.id),
358
374
  ...options.linearProjects?.length ? { projects: [...options.linearProjects] } : {},
359
- ...viewerId != null ? { assignees: [viewerId] } : {},
375
+ ...viewerId != null ? viewerScope(viewerId, options.filters) : {},
360
376
  states: toProviderIssueStates(options.state)
361
377
  }, options.sort, { providerId: this.id });
362
378
  if (drain.truncated || drain.metadata != null && drain.metadata.completeness !== "complete") return;
@@ -381,11 +397,12 @@ var LinearIntegration = class extends IssuesIntegration {
381
397
  * Follows `getIssues`' cursor up to {@link maxPagesPerRequest}. `truncated` is set when that backstop stopped
382
398
  * the drain with more pages still available, when the provider stalled its cursor or flagged its paging as
383
399
  * truncated, or when a page after the first failed, which is recorded at `failureScope` while the prefix is
384
- * kept. A first-page failure throws, so the caller sees a hard error rather than an empty success.
400
+ * kept. A failure before any issue was kept throws, so the caller sees a hard error rather than an empty success.
385
401
  */
386
402
  async drainIssues(session, filter, sort, failureScope) {
387
403
  const api = await this.getProvidersApi();
388
404
  const assignees = filter.assignees?.length ? filter.assignees : void 0;
405
+ const syncedCreators = filter.githubSyncedCreators?.length ? filter.githubSyncedCreators : void 0;
389
406
  let cursor;
390
407
  let hasMore;
391
408
  let requestCount = 0;
@@ -415,7 +432,7 @@ var LinearIntegration = class extends IssuesIntegration {
415
432
  collectionMetadata = mergeCollectionMetadata(collectionMetadata, result.metadata);
416
433
  for (const issue of result.values) {
417
434
  const shape = toIssueShape(issue, this, { projection: "project" });
418
- if (shape != null && (assignees == null || shape.assignees?.some((a) => assignees.includes(a.id)))) issues.push(shape);
435
+ if (shape != null && (assignees == null || shape.assignees?.some((a) => assignees.includes(a.id)) || syncedCreators != null && shape.author != null && syncedCreators.includes(shape.author.id) && shape.syncedWith?.length !== 0)) issues.push(shape);
419
436
  }
420
437
  if (hasMore && (nextCursor == null || nextCursor === cursor)) {
421
438
  truncated = true;