@mastra/factory 0.24.0-alpha.11 → 0.24.0-alpha.13

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 (165) hide show
  1. package/README.md +4 -4
  2. package/dist/environment/build-runner.js +2 -1
  3. package/dist/environment/build-runner.js.map +1 -1
  4. package/dist/environment/build-workflow.js +1 -1
  5. package/dist/environment/build-workflow.js.map +1 -1
  6. package/dist/environment/build.js +16 -14
  7. package/dist/environment/build.js.map +1 -1
  8. package/dist/factory.d.ts.map +1 -1
  9. package/dist/factory.js +25 -1
  10. package/dist/factory.js.map +1 -1
  11. package/dist/integrations/github/app-identity.js.map +1 -1
  12. package/dist/integrations/github/integration.js +1 -1
  13. package/dist/integrations/github/integration.js.map +1 -1
  14. package/dist/integrations/github/reconcile-worker.js +1 -1
  15. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  16. package/dist/integrations/github/routes.d.ts.map +1 -1
  17. package/dist/integrations/github/routes.js +6 -3
  18. package/dist/integrations/github/routes.js.map +1 -1
  19. package/dist/integrations/github/rules.js +3 -2
  20. package/dist/integrations/github/rules.js.map +1 -1
  21. package/dist/integrations/github/sandbox.js +2 -2
  22. package/dist/integrations/github/sandbox.js.map +1 -1
  23. package/dist/integrations/gitlab/api.js.map +1 -1
  24. package/dist/integrations/gitlab/integration.js.map +1 -1
  25. package/dist/integrations/gitlab/reconciliation-config.js +2 -1
  26. package/dist/integrations/gitlab/reconciliation-config.js.map +1 -1
  27. package/dist/integrations/gitlab/rules.js.map +1 -1
  28. package/dist/integrations/incidentio/api.js.map +1 -1
  29. package/dist/integrations/incidentio/integration.js.map +1 -1
  30. package/dist/integrations/incidentio/rules.js.map +1 -1
  31. package/dist/integrations/issue-reconcile-worker.js +1 -1
  32. package/dist/integrations/issue-reconcile-worker.js.map +1 -1
  33. package/dist/integrations/issue-reconciler.js +1 -1
  34. package/dist/integrations/issue-reconciler.js.map +1 -1
  35. package/dist/integrations/jira/api.js.map +1 -1
  36. package/dist/integrations/jira/integration.js.map +1 -1
  37. package/dist/integrations/jira/rules.js.map +1 -1
  38. package/dist/integrations/linear/integration.js.map +1 -1
  39. package/dist/integrations/linear/rules.js.map +1 -1
  40. package/dist/integrations/platform/api-client.js.map +1 -1
  41. package/dist/integrations/platform/github/event-worker.js +1 -1
  42. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  43. package/dist/integrations/platform/github/integration.js +2 -2
  44. package/dist/integrations/platform/github/integration.js.map +1 -1
  45. package/dist/integrations/platform/gitlab/event-worker.js.map +1 -1
  46. package/dist/integrations/platform/gitlab/integration.js.map +1 -1
  47. package/dist/integrations/platform/incidentio/integration.js +2 -1
  48. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  49. package/dist/integrations/platform/jira/integration.js.map +1 -1
  50. package/dist/integrations/platform/linear/event-worker.js +1 -1
  51. package/dist/integrations/platform/linear/event-worker.js.map +1 -1
  52. package/dist/integrations/platform/linear/integration.js.map +1 -1
  53. package/dist/integrations/slack/connect-route.js +1 -1
  54. package/dist/integrations/slack/connect-route.js.map +1 -1
  55. package/dist/integrations/slack/feed-publisher.js +1 -1
  56. package/dist/integrations/slack/feed-publisher.js.map +1 -1
  57. package/dist/integrations/slack/integration.js +1 -1
  58. package/dist/integrations/slack/integration.js.map +1 -1
  59. package/dist/integrations/slack/slack.js +2 -1
  60. package/dist/integrations/slack/slack.js.map +1 -1
  61. package/dist/integrations/workos/integration.js.map +1 -1
  62. package/dist/packages/_internals/workspace/dist/index.js +18 -1
  63. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  64. package/dist/routes/attention-activity.js.map +1 -1
  65. package/dist/routes/attention-parked.js.map +1 -1
  66. package/dist/routes/attention-providers.js.map +1 -1
  67. package/dist/routes/automation-runs.js.map +1 -1
  68. package/dist/routes/contracts.js +1 -1
  69. package/dist/routes/contracts.js.map +1 -1
  70. package/dist/routes/custom-provider-source.js.map +1 -1
  71. package/dist/routes/fs.d.ts +31 -23
  72. package/dist/routes/fs.d.ts.map +1 -1
  73. package/dist/routes/fs.js +218 -64
  74. package/dist/routes/fs.js.map +1 -1
  75. package/dist/routes/intake.js +2 -1
  76. package/dist/routes/intake.js.map +1 -1
  77. package/dist/routes/knowledge.js +1 -1
  78. package/dist/routes/knowledge.js.map +1 -1
  79. package/dist/routes/oauth.js +3 -2
  80. package/dist/routes/oauth.js.map +1 -1
  81. package/dist/routes/projects.js +32 -30
  82. package/dist/routes/projects.js.map +1 -1
  83. package/dist/routes/session-access.d.ts +55 -0
  84. package/dist/routes/session-access.d.ts.map +1 -0
  85. package/dist/routes/session-access.js +165 -0
  86. package/dist/routes/session-access.js.map +1 -0
  87. package/dist/routes/skills.js.map +1 -1
  88. package/dist/routes/source-control-sessions.d.ts +5 -0
  89. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  90. package/dist/routes/source-control-sessions.js +87 -10
  91. package/dist/routes/source-control-sessions.js.map +1 -1
  92. package/dist/routes/surface.d.ts +5 -0
  93. package/dist/routes/surface.d.ts.map +1 -1
  94. package/dist/routes/surface.js +10 -4
  95. package/dist/routes/surface.js.map +1 -1
  96. package/dist/routes/tenant-credentials.js.map +1 -1
  97. package/dist/routes/work-items.js.map +1 -1
  98. package/dist/rules/binding-context.js +4 -2
  99. package/dist/rules/binding-context.js.map +1 -1
  100. package/dist/rules/dispatcher.js +4 -4
  101. package/dist/rules/dispatcher.js.map +1 -1
  102. package/dist/rules/start-coordinator.js.map +1 -1
  103. package/dist/rules/tools.js +2 -2
  104. package/dist/rules/tools.js.map +1 -1
  105. package/dist/rules/transition-service.js.map +1 -1
  106. package/dist/sandbox/callback-factory-sandbox.js.map +1 -1
  107. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  108. package/dist/sandbox/session-retirement.js.map +1 -1
  109. package/dist/session/live-sessions.js.map +1 -1
  110. package/dist/session/memory-settings-hydration.js +2 -1
  111. package/dist/session/memory-settings-hydration.js.map +1 -1
  112. package/dist/session/resource-migration.d.ts +18 -0
  113. package/dist/session/resource-migration.d.ts.map +1 -1
  114. package/dist/session/resource-migration.js +24 -1
  115. package/dist/session/resource-migration.js.map +1 -1
  116. package/dist/session/session-threads.d.ts +9 -0
  117. package/dist/session/session-threads.d.ts.map +1 -1
  118. package/dist/session/session-threads.js +24 -4
  119. package/dist/session/session-threads.js.map +1 -1
  120. package/dist/spa-static.js.map +1 -1
  121. package/dist/state-signing.js +1 -1
  122. package/dist/state-signing.js.map +1 -1
  123. package/dist/storage/domains/audit/base.js.map +1 -1
  124. package/dist/storage/domains/audit/domain.js.map +1 -1
  125. package/dist/storage/domains/channel-identity/base.js.map +1 -1
  126. package/dist/storage/domains/comments/base.js.map +1 -1
  127. package/dist/storage/domains/comments/domain.js.map +1 -1
  128. package/dist/storage/domains/comments/feed-context.js.map +1 -1
  129. package/dist/storage/domains/credentials/base.js.map +1 -1
  130. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  131. package/dist/storage/domains/intake/base.js.map +1 -1
  132. package/dist/storage/domains/integrations/base.js.map +1 -1
  133. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  134. package/dist/storage/domains/model-defaults/base.js.map +1 -1
  135. package/dist/storage/domains/projects/base.js.map +1 -1
  136. package/dist/storage/domains/queue-health/base.js.map +1 -1
  137. package/dist/storage/domains/source-control/base.d.ts +15 -0
  138. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  139. package/dist/storage/domains/source-control/base.js +29 -0
  140. package/dist/storage/domains/source-control/base.js.map +1 -1
  141. package/dist/storage/domains/source-control/inmemory.d.ts +5 -1
  142. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  143. package/dist/storage/domains/source-control/inmemory.js +7 -0
  144. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  145. package/dist/storage/domains/work-items/base.js +2 -1
  146. package/dist/storage/domains/work-items/base.js.map +1 -1
  147. package/dist/storage/domains/work-items/board-version.js.map +1 -1
  148. package/dist/storage/domains/work-items/metrics.js.map +1 -1
  149. package/dist/tools/messaging-tools.d.ts.map +1 -1
  150. package/dist/tools/messaging-tools.js +201 -73
  151. package/dist/tools/messaging-tools.js.map +1 -1
  152. package/dist/tools/run-watch.d.ts +2 -0
  153. package/dist/tools/run-watch.d.ts.map +1 -1
  154. package/dist/tools/run-watch.js +42 -2
  155. package/dist/tools/run-watch.js.map +1 -1
  156. package/dist/tools/session-tools.d.ts.map +1 -1
  157. package/dist/tools/session-tools.js +25 -10
  158. package/dist/tools/session-tools.js.map +1 -1
  159. package/dist/tools/sessions-state-processor.js +2 -2
  160. package/dist/tools/sessions-state-processor.js.map +1 -1
  161. package/dist/workspace.d.ts +5 -2
  162. package/dist/workspace.d.ts.map +1 -1
  163. package/dist/workspace.js +26 -13
  164. package/dist/workspace.js.map +1 -1
  165. package/package.json +9 -9
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":["#orgIdByResourceId","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#versionControlContext","#versionControlContextForInstallation","#projects","#auth","#sourceControl","#integrationStorage","#resolveSourceReference","#contextForReference","#toIntakeSource","#listIssuePage","#toIntakeIssue","#resolveRequest","#listAllNotes","#accessToken","#accessTokenType","#baseUrl","#webhookSecret","#context"],"sources":["../../../src/integrations/gitlab/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../capabilities/intake.js';\nimport type { RouteAuth } from '../../routes/route.js';\nimport type { IntegrationStorageHandle } from '../../storage/domains/integrations/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { IssueReconcileWorker } from '../issue-reconcile-worker.js';\nimport { buildGitLabAgentTools } from './agent-tools.js';\nimport {\n GITLAB_ISSUES_PAGE_SIZE,\n GITLAB_NOTES_PAGE_SIZE,\n GITLAB_PROJECTS_PAGE_SIZE,\n GitLabApiClient,\n GitLabApiError,\n} from './api.js';\nimport type { GitLabIssue, GitLabNote, GitLabProject } from './api.js';\nimport { resolveGitLabRules } from './default-rules.js';\nimport type { GitLabEventRules, GitLabRuleOverrides } from './default-rules.js';\nimport { attachGitLabReconciler } from './reconciler.js';\nimport { gitlabReconciliationEnabled, gitlabReconciliationInterval } from './reconciliation-config.js';\nimport { buildGitLabRoutes } from './routes.js';\nimport { attachGitLabRules } from './rules.js';\nimport {\n createGitLabSubscriptionTools,\n parseCreatedMergeRequest,\n subscribeCurrentSessionToMergeRequest,\n} from './session-subscriptions.js';\nimport type { GitLabSubscriptionStorage } from './subscriptions.js';\nimport { buildGitLabVersionControl } from './version-control.js';\n\ninterface GitLabConnectionContext {\n id: string;\n label: string | null;\n api: GitLabApiClient;\n connection: IntegrationConnection;\n host: string;\n webBaseUrl?: string;\n /** Resolves a fresh provider token for a single brokered git operation. */\n repositoryAccessToken?: () => Promise<string>;\n}\n\ninterface GitLabSourceReference {\n /** Present only on legacy v1 ids; authentication is not resource identity. */\n connectionId?: string;\n /** Canonical v2 ids use the GitLab instance host plus immutable project id. */\n host?: string;\n projectId: string;\n /** Legacy ids carry display metadata; v2 resolves it from GitLab. */\n projectPath?: string;\n}\n\ninterface ResolvedGitLabSourceReference extends GitLabSourceReference {\n connectionId: string;\n host: string;\n projectPath: string;\n}\n\ninterface GitLabIssueReference extends GitLabSourceReference {\n issueIid: number;\n}\n\nexport interface GitLabMergeRequestReference {\n version: 1;\n host: string;\n projectId: number;\n mergeRequestIid: number;\n}\n\ninterface GitLabPageCursor {\n source: number;\n page: number;\n}\n\nexport interface GitLabStatusConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\nconst DIRECT_CONNECTION_ID = 'direct';\nconst GITLAB_CONNECTION_TOKEN_PREFIX = 'gitlab-connection:';\nconst GITLAB_SOURCE_PREFIX = 'gitlab-project:';\nconst GITLAB_ISSUE_PREFIX = 'gitlab-issue:';\nconst GITLAB_MERGE_REQUEST_PREFIX = 'gitlab-pr:';\nexport const GITLAB_TRUSTED_ACCESS_LEVEL = 30;\n// Bound issue detail reads to 2,000 notes. GitLab does not expose truncation through the Intake contract.\nconst MAX_NOTES_PAGES = 20;\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport abstract class GitLabIntegrationBase implements FactoryIntegration {\n readonly id = 'gitlab';\n #projects: FactoryProjectsStorage | undefined;\n #sourceControl: SourceControlStorageHandle | undefined;\n #integrationStorage: GitLabSubscriptionStorage | undefined;\n #auth: RouteAuth | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n readonly rules: GitLabEventRules;\n\n constructor(rules?: GitLabRuleOverrides) {\n this.rules = resolveGitLabRules(rules);\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n readonly versionControl = buildGitLabVersionControl({\n contextForConnection: connection => this.#versionControlContext(connection),\n contextForStoredInstallation: (connection, host) => this.#versionControlContextForInstallation(connection, host),\n });\n initialize({\n storage,\n projects,\n auth,\n sourceControl,\n }: {\n storage?: IntegrationStorageHandle;\n projects: FactoryProjectsStorage;\n auth: RouteAuth;\n sourceControl?: SourceControlStorageHandle;\n }): void {\n this.#projects = projects;\n this.#auth = auth;\n this.#sourceControl = sourceControl;\n this.#integrationStorage = storage as GitLabSubscriptionStorage | undefined;\n }\n\n /** Generic integration persistence (merge-request subscriptions). */\n get integrationStorage(): GitLabSubscriptionStorage {\n if (!this.#integrationStorage) {\n throw new Error(`${this.constructor.name} is not initialized — the factory binds storage during prepare().`);\n }\n return this.#integrationStorage;\n }\n\n /** Source-control rows for this integration; absent when the host runs without them. */\n get sourceControlStorage(): SourceControlStorageHandle | undefined {\n return this.#sourceControl;\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) {\n throw new Error(`${this.constructor.name} is not initialized — the factory binds storage during prepare().`);\n }\n return this.#projects;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n const orgId = project?.orgId ?? null;\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n } catch {\n return null;\n }\n }\n\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n }\n\n abstract hasActiveConnections(): Promise<boolean>;\n abstract authFailureMessage(): string;\n protected abstract activeContexts(): Promise<GitLabConnectionContext[]>;\n protected abstract contextById(connectionId: string): Promise<GitLabConnectionContext>;\n\n /** Resolve only a GitLab repository linked to this Factory in this organization. */\n async getLinkedRepository(input: { orgId: string; factoryProjectId: string; projectRepositoryId: string }) {\n if (!this.#sourceControl) return null;\n const connections = await this.#sourceControl.connections.list({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n });\n for (const connection of connections) {\n if (connection.integrationId !== 'gitlab') continue;\n const links = await this.#sourceControl.projectRepositories.list({\n orgId: input.orgId,\n connectionId: connection.id,\n });\n const link = links.find(candidate => candidate.id === input.projectRepositoryId);\n if (!link) continue;\n const repository = await this.#sourceControl.repositories.get({ orgId: input.orgId, id: link.repositoryId });\n const installation = await this.#sourceControl.installations.get({\n orgId: input.orgId,\n id: connection.installationId,\n });\n if (!repository || !installation) return null;\n const host = installation.providerMetadata.host;\n if (typeof host !== 'string' || !host) return null;\n return { repository, host: normalizeGitLabHost(host) };\n }\n return null;\n }\n\n async getProjectMemberAccessLevel(\n connectionId: string,\n projectId: string,\n username: string,\n ): Promise<number | undefined> {\n const normalized = username.trim().toLowerCase();\n if (!normalized) return undefined;\n const members = await (await this.contextById(connectionId)).api.listProjectMembers(projectId, { query: username });\n const member = members.find(\n candidate => candidate.username.toLowerCase() === normalized && candidate.state !== 'blocked',\n );\n return member?.access_level;\n }\n\n async getWorkItemAuthorUsername(\n connectionId: string,\n projectId: string,\n kind: 'issue' | 'merge_request',\n iid: number,\n ): Promise<string | undefined> {\n const api = (await this.contextById(connectionId)).api;\n const item = kind === 'issue' ? await api.getIssue(projectId, iid) : await api.getMergeRequest(projectId, iid);\n return item.author?.username;\n }\n\n async isProjectMemberTrustedForSource(sourceId: string, username: string): Promise<boolean> {\n const reference = decodeSourceId(sourceId);\n if (!reference) throw new GitLabApiError('GitLab source identity is invalid.', 400);\n const { context, source } = await this.#resolveSourceReference(reference);\n const accessLevel = await this.getProjectMemberAccessLevel(context.id, source.projectId, username);\n return (accessLevel ?? 0) >= GITLAB_TRUSTED_ACCESS_LEVEL;\n }\n\n async getIssueForFactoryProject(input: {\n orgId: string;\n factoryProjectId: string;\n issueId: string;\n }): Promise<IntakeIssueDetail | null> {\n if (!this.#sourceControl) throw new GitLabApiError('GitLab source control is unavailable.', 503);\n const reference = decodeIssueReference(input.issueId);\n const locator = parseIssueLocator(input.issueId);\n if (!reference && !locator) {\n throw new GitLabApiError('GitLab issue must include a project and issue IID.', 400);\n }\n\n const normalizePath = (value: string) => value.replace(/^\\/+|\\/+$/g, '').toLowerCase();\n const matches: Array<{\n connectionId: string;\n projectId: string;\n projectPath: string;\n }> = [];\n const connections = await this.#sourceControl.connections.list({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n });\n for (const connection of connections) {\n const installation = await this.#sourceControl.installations.get({\n orgId: input.orgId,\n id: connection.installationId,\n });\n if (!installation) continue;\n for (const projectRepository of await this.#sourceControl.projectRepositories.list({\n orgId: input.orgId,\n connectionId: connection.id,\n })) {\n const repository = await this.#sourceControl.repositories.get({\n orgId: input.orgId,\n id: projectRepository.repositoryId,\n });\n if (!repository) continue;\n if (reference?.projectId && repository.externalId !== reference.projectId) continue;\n if (reference?.projectPath && normalizePath(repository.slug) !== normalizePath(reference.projectPath)) continue;\n if (locator?.projectPath && normalizePath(repository.slug) !== normalizePath(locator.projectPath)) continue;\n if (reference?.connectionId && installation.externalId !== reference.connectionId) continue;\n const installationHost =\n typeof installation.providerMetadata.host === 'string'\n ? normalizeGitLabHost(installation.providerMetadata.host)\n : null;\n if (reference?.host && installationHost !== normalizeGitLabHost(reference.host)) continue;\n if (locator?.host && installationHost !== normalizeGitLabHost(locator.host)) continue;\n matches.push({\n connectionId: installation.externalId,\n projectId: repository.externalId,\n projectPath: repository.slug,\n });\n }\n }\n const match = matches.sort((left, right) => left.connectionId.localeCompare(right.connectionId))[0];\n if (!match) throw new GitLabApiError('GitLab issue is outside the active Factory project.', 404);\n return this.intake.getIssue({\n connection: gitlabConnection(match.connectionId),\n sourceId: encodeSourceId(match),\n issueId: input.issueId,\n });\n }\n\n /** Platform mode exposes org connections; direct mode returns undefined. */\n async statusConnections(): Promise<GitLabStatusConnection[] | undefined> {\n return undefined;\n }\n\n /** Direct mode verifies its configured credential; Platform mode uses connection status. */\n async verifyStatus(): Promise<void> {\n // Platform connection state is authoritative unless a provider overrides this probe.\n }\n\n protected get webhookSecret(): string | undefined {\n return undefined;\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!gitlabReconciliationEnabled()) return [];\n const reconcile = attachGitLabReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = gitlabReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n routes(ctx: IntegrationContext): ApiRoute[] {\n const ingestFactoryEvent = attachGitLabRules(this, ctx);\n return buildGitLabRoutes({\n gitlab: this,\n auth: ctx.auth,\n intake: ctx.storage?.intake,\n emitAudit: ctx.hooks?.emitAudit,\n sandbox: ctx.sandbox,\n webhookSecret: this.webhookSecret,\n ingestFactoryEvent,\n ...(ctx.controller ? { controller: ctx.controller } : {}),\n });\n }\n\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildGitLabAgentTools({ requestContext: args.requestContext, gitlab: this });\n }\n\n sessionTools({ requestContext }: { requestContext: RequestContext }): IntegrationTools {\n return createGitLabSubscriptionTools(requestContext, this);\n }\n\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const mergeRequestUrl = parseCreatedMergeRequest(toolContext);\n if (!mergeRequestUrl || !requestContext) return;\n await subscribeCurrentSessionToMergeRequest(requestContext, mergeRequestUrl, 'auto-create-change-request', this);\n }\n\n abstract diagnostics(): Record<string, unknown>;\n\n /** Prefer the configured direct token over an older Platform installation on the same host. */\n async resolveActiveConnectionForHost(storedConnectionId: string, host: string): Promise<string> {\n if (this.diagnostics().mode !== 'direct') return storedConnectionId;\n const [direct] = await this.activeContexts();\n return direct && normalizeGitLabHost(direct.host) === normalizeGitLabHost(host) ? direct.id : storedConnectionId;\n }\n\n async #versionControlContext(connection: IntegrationConnection): Promise<GitLabConnectionContext> {\n const connectionId = connectionIdFromConnection(connection);\n if (connectionId) return this.contextById(connectionId);\n const contexts = await this.activeContexts();\n if (contexts.length !== 1) {\n throw new GitLabApiError(\n 'GitLab version-control requests must identify a connection when multiple GitLab accounts are connected.',\n 400,\n );\n }\n return contexts[0]!;\n }\n async #versionControlContextForInstallation(\n connection: IntegrationConnection,\n host: string | undefined,\n ): Promise<GitLabConnectionContext> {\n // A direct PAT intentionally overrides Platform credentials. Old links can\n // retain their Platform connection id; use the PAT only for the same host.\n if (this.diagnostics().mode === 'direct' && host) {\n const [direct] = await this.activeContexts();\n if (direct && normalizeGitLabHost(host) === normalizeGitLabHost(direct.host)) return direct;\n }\n return this.#versionControlContext(connection);\n }\n async #contextForReference(reference: GitLabSourceReference): Promise<GitLabConnectionContext> {\n if (reference.connectionId) return this.contextById(reference.connectionId);\n const contexts = (await this.activeContexts()).filter(\n context => !reference.host || normalizeGitLabHost(context.host) === normalizeGitLabHost(reference.host),\n );\n if (contexts.length === 0) {\n throw new GitLabApiError('No active GitLab connection can access this source.', 401);\n }\n if (contexts.length === 1) return contexts[0]!;\n\n if (this.#sourceControl) {\n const linkedConnectionIds = new Set(\n (await this.#sourceControl.projectRepositories.listConfiguredExternalKeys())\n .filter(key => key.repositoryExternalId === reference.projectId)\n .map(key => key.installationExternalId),\n );\n const linked = contexts.filter(context => linkedConnectionIds.has(context.id));\n if (linked.length > 0) return linked.sort((left, right) => left.id.localeCompare(right.id))[0]!;\n }\n\n for (const context of contexts.sort((left, right) => left.id.localeCompare(right.id))) {\n try {\n await context.api.getProject(reference.projectId);\n return context;\n } catch (error) {\n if (!(error instanceof GitLabApiError) || ![401, 403, 404].includes(error.status ?? 0)) throw error;\n // This credential cannot access the project; try another active account for the same canonical source.\n }\n }\n throw new GitLabApiError('No active GitLab connection can access this source.', 404);\n }\n\n async #resolveSourceReference(\n reference: GitLabSourceReference,\n ): Promise<{ context: GitLabConnectionContext; source: ResolvedGitLabSourceReference }> {\n const context = await this.#contextForReference(reference);\n const projectPath =\n reference.projectPath ?? (await context.api.getProject(reference.projectId)).path_with_namespace;\n return {\n context,\n source: {\n connectionId: context.id,\n host: normalizeGitLabHost(reference.host ?? context.host),\n projectId: reference.projectId,\n projectPath,\n },\n };\n }\n\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n const reference = decodeIssueReference(externalSource.externalId);\n if (!reference) return null;\n const { context, source } = await this.#resolveSourceReference(reference);\n return {\n connection: context.connection,\n sourceId: encodeSourceId(source),\n issueId: String(reference.issueIid),\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const sources = new Map<string, IntakeSource>();\n for (const context of await this.activeContexts()) {\n for (let page = 1; ; page++) {\n const projects = await context.api.listProjects({ page });\n for (const project of projects) {\n const source = this.#toIntakeSource(context, project);\n if (!sources.has(source.id)) sources.set(source.id, source);\n }\n if (projects.length < GITLAB_PROJECTS_PAGE_SIZE) break;\n }\n }\n return [...sources.values()];\n }\n\n async #listItems(input: ListIntakeItemsInput): Promise<IntakeItemPage> {\n const result = await this.#listIssuePage(input.sourceIds, input.cursor);\n return {\n items: result.issues.map(({ issue, source, context }) => ({\n source: {\n type: 'issue',\n externalId: encodeIssueReference({ ...source, issueIid: issue.iid }),\n url: issue.web_url,\n },\n sourceId: encodeSourceId(source),\n title: `${source.projectPath}#${issue.iid}: ${issue.title}`,\n status: issue.state,\n labels: issue.labels ?? [],\n assignee: displayName(issue.assignee),\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n metadata: {\n identifier: `${source.projectPath}#${issue.iid}`,\n projectId: source.projectId,\n projectPath: source.projectPath,\n connectionId: context.id,\n accountLabel: context.label,\n labelColors: Object.fromEntries((issue.labelDetails ?? []).map(label => [label.name, label.color])),\n },\n })),\n nextCursor: result.nextCursor,\n };\n }\n\n async #listIssues(input: ListIntakeIssuesInput) {\n const result = await this.#listIssuePage(input.sourceIds, input.cursor, input.labels);\n return {\n issues: result.issues.map(({ issue, source }) => ({\n ...this.#toIntakeIssue(issue, source.projectPath),\n sourceId: encodeSourceId(source),\n })),\n nextCursor: result.nextCursor,\n };\n }\n\n async #listIssuePage(sourceIds: string[], cursor?: string, labels?: string[]) {\n const sources = sourceIds.map(decodeSourceId).filter((source): source is GitLabSourceReference => source !== null);\n if (sources.length === 0) return { issues: [], nextCursor: null };\n const current = decodePageCursor(cursor);\n if (!current || current.source >= sources.length) return { issues: [], nextCursor: null };\n\n for (let sourceIndex = current.source; sourceIndex < sources.length; sourceIndex++) {\n const { source, context } = await this.#resolveSourceReference(sources[sourceIndex]!);\n const page = sourceIndex === current.source ? current.page : 1;\n const issues = await context.api.listIssues(source.projectId, { page, labels });\n const nextCursor =\n issues.length === GITLAB_ISSUES_PAGE_SIZE\n ? encodePageCursor({ source: sourceIndex, page: page + 1 })\n : sourceIndex + 1 < sources.length\n ? encodePageCursor({ source: sourceIndex + 1, page: 1 })\n : null;\n return { issues: issues.map(issue => ({ issue, source, context })), nextCursor };\n }\n return { issues: [], nextCursor: null };\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: GitLabIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n const notes = await this.#listAllNotes(resolved.context.api, resolved.projectId, resolved.issueIid);\n return {\n ...this.#toIntakeIssue(issue, resolved.projectPath),\n description: issue.description?.trim() || null,\n comments: notes\n .filter(note => !note.system)\n .map(note => ({ author: displayName(note.author), body: note.body, createdAt: note.created_at })),\n };\n }\n\n async #listAllNotes(api: GitLabApiClient, projectId: string, issueIid: number): Promise<GitLabNote[]> {\n const notes: GitLabNote[] = [];\n for (let page = 1; page <= MAX_NOTES_PAGES; page++) {\n const result = await api.listNotes(projectId, issueIid, { page });\n notes.push(...result);\n if (result.length < GITLAB_NOTES_PAGE_SIZE) break;\n }\n return notes;\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#resolveRequest(input);\n try {\n const issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n const note = await resolved.context.api.createNote(resolved.projectId, resolved.issueIid, input.body);\n return { id: String(note.id), url: `${issue.web_url}#note_${note.id}` };\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: GitLabIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n const stateEvent = targetStateEvent(input);\n if (!stateEvent) return null;\n if ((stateEvent === 'close' && issue.state === 'closed') || (stateEvent === 'reopen' && issue.state === 'opened')) {\n return this.#toIntakeIssue(issue, resolved.projectPath);\n }\n const updated = await resolved.context.api.updateIssueState(resolved.projectId, resolved.issueIid, stateEvent);\n return this.#toIntakeIssue(updated, resolved.projectPath);\n }\n\n async #resolveRequest(input: GetIntakeIssueInput): Promise<{\n context: GitLabConnectionContext;\n projectId: string;\n projectPath: string;\n issueIid: number;\n }> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const locator = parseIssueLocator(input.issueId);\n const reference = issueReference ?? source;\n let context: GitLabConnectionContext;\n if (reference) {\n context = await this.#contextForReference(reference);\n } else {\n const connectionId = connectionIdFromConnection(input.connection);\n const contexts = connectionId ? [await this.contextById(connectionId)] : await this.activeContexts();\n if (contexts.length !== 1) {\n throw new GitLabApiError('GitLab issue reference must resolve to exactly one active connection.', 400);\n }\n context = contexts[0]!;\n }\n const projectId = issueReference?.projectId ?? source?.projectId ?? locator?.projectPath;\n const issueIid = issueReference?.issueIid ?? locator?.issueIid ?? parsePositiveInteger(input.issueId);\n if (!projectId || !issueIid) {\n throw new GitLabApiError(\n 'GitLab issue must include a project and issue IID (for example, group/project#42).',\n 400,\n );\n }\n const projectPath =\n issueReference?.projectPath ??\n source?.projectPath ??\n locator?.projectPath ??\n (await context.api.getProject(projectId)).path_with_namespace;\n return { context, projectId, projectPath, issueIid };\n }\n\n #toIntakeSource(context: GitLabConnectionContext, project: GitLabProject): IntakeSource {\n const reference: GitLabSourceReference = {\n host: normalizeGitLabHost(context.host),\n projectId: String(project.id),\n projectPath: project.path_with_namespace,\n };\n return {\n id: encodeSourceId(reference),\n name: project.path_with_namespace,\n type: 'project',\n metadata: {\n projectId: String(project.id),\n projectPath: project.path_with_namespace,\n defaultBranch: project.default_branch ?? null,\n connectionId: context.id,\n accountLabel: context.label,\n url: project.web_url,\n },\n };\n }\n\n #toIntakeIssue(issue: GitLabIssue, projectPath: string): IntakeIssue {\n const assignees = issue.assignees?.map(displayName).filter((name): name is string => name !== null) ?? [];\n return {\n id: String(issue.iid),\n identifier: `${projectPath}#${issue.iid}`,\n title: issue.title,\n url: issue.web_url,\n author: displayName(issue.author),\n authorUsername: issue.author?.username ?? null,\n state: issue.state,\n stateType: issue.state === 'closed' ? 'completed' : 'unstarted',\n priority: typeof issue.weight === 'number' ? String(issue.weight) : null,\n assignee: displayName(issue.assignee) ?? assignees[0] ?? null,\n assignees,\n source: projectPath,\n labels: issue.labels ?? [],\n labelColors: Object.fromEntries((issue.labelDetails ?? []).map(label => [label.name, label.color])),\n commentCount: issue.user_notes_count ?? null,\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n };\n }\n}\n\nexport type GitLabAccessTokenType = 'personal' | 'group';\n\nexport interface GitLabIntegrationConfig {\n rules?: GitLabRuleOverrides;\n accessToken?: string;\n /** Defaults to `personal`; both token types authenticate identically but have different resource reach. */\n accessTokenType?: GitLabAccessTokenType;\n baseUrl?: string;\n webhookSecret?: string;\n fetchImpl?: typeof fetch;\n}\n\nconst DIRECT_CONNECTION_TOKEN = 'gitlab-direct-access-token';\n\nexport class GitLabIntegration extends GitLabIntegrationBase {\n readonly #accessToken: string;\n readonly #accessTokenType: GitLabAccessTokenType;\n readonly #baseUrl: string;\n readonly #webhookSecret: string | undefined;\n readonly #context: GitLabConnectionContext;\n\n constructor(config: GitLabIntegrationConfig = {}) {\n super(config.rules);\n const accessToken = config.accessToken?.trim() || process.env.GITLAB_ACCESS_TOKEN?.trim();\n if (!accessToken) {\n throw new Error('GitLabIntegration: missing required GITLAB_ACCESS_TOKEN.');\n }\n this.#accessToken = accessToken;\n this.#accessTokenType = parseAccessTokenType(\n config.accessTokenType ?? process.env.GITLAB_ACCESS_TOKEN_TYPE?.trim(),\n );\n this.#baseUrl = (config.baseUrl ?? process.env.GITLAB_BASE_URL?.trim() ?? 'https://gitlab.com').replace(/\\/+$/, '');\n this.#webhookSecret = config.webhookSecret?.trim() || process.env.GITLAB_WEBHOOK_SECRET?.trim() || undefined;\n const api = new GitLabApiClient({\n baseUrl: this.#baseUrl,\n accessToken: this.#accessToken,\n ...(config.fetchImpl ? { fetchImpl: config.fetchImpl } : {}),\n });\n this.#context = {\n id: DIRECT_CONNECTION_ID,\n label: new URL(this.#baseUrl).host,\n api,\n connection: { type: 'oauth', accessToken: DIRECT_CONNECTION_TOKEN },\n host: new URL(this.#baseUrl).host,\n webBaseUrl: this.#baseUrl,\n repositoryAccessToken: async () => this.#accessToken,\n };\n }\n\n protected override get webhookSecret(): string | undefined {\n return this.#webhookSecret;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return true;\n }\n\n override async verifyStatus(): Promise<void> {\n await this.#context.api.getCurrentUser();\n }\n\n authFailureMessage(): string {\n return `GitLab rejected the configured ${this.#accessTokenType} access token. Check the token and its scopes.`;\n }\n\n protected async activeContexts(): Promise<GitLabConnectionContext[]> {\n return [this.#context];\n }\n\n protected async contextById(connectionId: string): Promise<GitLabConnectionContext> {\n if (connectionId !== DIRECT_CONNECTION_ID) {\n throw new GitLabApiError('GitLab connection is unavailable.', 401);\n }\n return this.#context;\n }\n\n diagnostics(): Record<string, unknown> {\n return {\n configured: true,\n mode: 'direct',\n accessTokenType: this.#accessTokenType,\n endpointHost: new URL(this.#baseUrl).host,\n webhookConfigured: Boolean(this.#webhookSecret),\n };\n }\n}\n\nfunction parseAccessTokenType(value: string | undefined): GitLabAccessTokenType {\n const normalized = value?.trim().toLowerCase() || 'personal';\n if (normalized === 'personal' || normalized === 'group') return normalized;\n throw new Error(\"GitLabIntegration: GITLAB_ACCESS_TOKEN_TYPE must be 'personal' or 'group'.\");\n}\n\nexport function encodeSourceId(reference: GitLabSourceReference): string {\n if (reference.host) {\n return `${GITLAB_SOURCE_PREFIX}${encodeOpaque({\n version: 2,\n host: normalizeGitLabHost(reference.host),\n projectId: reference.projectId,\n })}`;\n }\n if (!reference.connectionId || !reference.projectPath) {\n throw new Error('GitLab source reference is missing its instance host.');\n }\n return `${GITLAB_SOURCE_PREFIX}${encodeOpaque(reference)}`;\n}\n\nexport function decodeSourceId(value: string): GitLabSourceReference | null {\n return decodeOpaque(value, GITLAB_SOURCE_PREFIX, isSourceReference);\n}\n\nexport function encodeIssueReference(reference: GitLabIssueReference): string {\n if (reference.host) {\n return `${GITLAB_ISSUE_PREFIX}${encodeOpaque({\n version: 2,\n host: normalizeGitLabHost(reference.host),\n projectId: reference.projectId,\n issueIid: reference.issueIid,\n })}`;\n }\n if (!reference.connectionId || !reference.projectPath) {\n throw new Error('GitLab issue reference is missing its instance host.');\n }\n return `${GITLAB_ISSUE_PREFIX}${encodeOpaque(reference)}`;\n}\n\nexport function decodeIssueReference(value: string): GitLabIssueReference | null {\n return decodeOpaque(value, GITLAB_ISSUE_PREFIX, isIssueReference);\n}\n\nexport function decodeMergeRequestReference(value: string): GitLabMergeRequestReference | null {\n return decodeOpaque(value, GITLAB_MERGE_REQUEST_PREFIX, isMergeRequestReference);\n}\n\nexport function gitlabConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${GITLAB_CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(GITLAB_CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(GITLAB_CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\nfunction encodeOpaque(value: unknown): string {\n return Buffer.from(JSON.stringify(value)).toString('base64url');\n}\n\nfunction decodeOpaque<T>(value: string, prefix: string, guard: (value: unknown) => value is T): T | null {\n if (!value.startsWith(prefix)) return null;\n try {\n const decoded = JSON.parse(Buffer.from(value.slice(prefix.length), 'base64url').toString('utf8')) as unknown;\n return guard(decoded) ? decoded : null;\n } catch {\n return null;\n }\n}\n\nexport function normalizeGitLabHost(host: string): string {\n return host.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nfunction isSourceReference(value: unknown): value is GitLabSourceReference {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const ref = value as Record<string, unknown>;\n if (typeof ref.projectId !== 'string' || ref.projectId.length === 0) return false;\n const canonical = ref.version === 2 && typeof ref.host === 'string' && normalizeGitLabHost(ref.host).length > 0;\n const legacy =\n typeof ref.connectionId === 'string' &&\n ref.connectionId.length > 0 &&\n typeof ref.projectPath === 'string' &&\n ref.projectPath.length > 0;\n return canonical || legacy;\n}\n\nfunction isIssueReference(value: unknown): value is GitLabIssueReference {\n return (\n isSourceReference(value) &&\n Number.isSafeInteger((value as GitLabIssueReference).issueIid) &&\n (value as GitLabIssueReference).issueIid > 0\n );\n}\n\nfunction isMergeRequestReference(value: unknown): value is GitLabMergeRequestReference {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const ref = value as Record<string, unknown>;\n return (\n ref.version === 1 &&\n typeof ref.host === 'string' &&\n normalizeGitLabHost(ref.host).length > 0 &&\n Number.isSafeInteger(ref.projectId) &&\n (ref.projectId as number) > 0 &&\n Number.isSafeInteger(ref.mergeRequestIid) &&\n (ref.mergeRequestIid as number) > 0\n );\n}\n\nfunction encodePageCursor(cursor: GitLabPageCursor): string {\n return encodeOpaque(cursor);\n}\n\nfunction decodePageCursor(value: string | undefined): GitLabPageCursor | null {\n if (!value) return { source: 0, page: 1 };\n try {\n const decoded = JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as Record<string, unknown>;\n if (!Number.isSafeInteger(decoded.source) || Number(decoded.source) < 0) return null;\n if (!Number.isSafeInteger(decoded.page) || Number(decoded.page) < 1) return null;\n return { source: Number(decoded.source), page: Number(decoded.page) };\n } catch {\n return null;\n }\n}\n\nfunction displayName(user: { name?: string | null; username: string } | null | undefined): string | null {\n return user?.name?.trim() || user?.username || null;\n}\n\nfunction parsePositiveInteger(value: string): number | null {\n if (!/^\\d+$/.test(value)) return null;\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction parseIssueLocator(value: string): {\n host?: string;\n projectPath: string;\n issueIid: number;\n} | null {\n const shorthand = value.match(/^(.+?)#(\\d+)$/);\n if (shorthand) {\n const issueIid = parsePositiveInteger(shorthand[2]!);\n const projectPath = shorthand[1]!.replace(/^\\/+|\\/+$/g, '');\n return issueIid && projectPath ? { projectPath, issueIid } : null;\n }\n try {\n const url = new URL(value);\n const match = url.pathname.match(/^\\/(.+)\\/-\\/issues\\/(\\d+)\\/?$/);\n if (!match) return null;\n const issueIid = parsePositiveInteger(match[2]!);\n if (!issueIid) return null;\n const projectPath = decodeURIComponent(match[1]!);\n return { host: url.host, projectPath, issueIid };\n } catch {\n return null;\n }\n}\n\nfunction targetStateEvent(input: UpdateIntakeIssueInput): 'close' | 'reopen' | null {\n if (input.state.kind === 'byType') {\n // GitLab issues have only opened/closed states, so canceled is intentionally mapped to close.\n return input.state.stateType === 'completed' || input.state.stateType === 'canceled' ? 'close' : 'reopen';\n }\n const name = input.state.name.trim().toLowerCase();\n if (name === 'closed' || name === 'close') return 'close';\n if (name === 'opened' || name === 'open' || name === 'reopen') return 'reopen';\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","names":[],"sources":["../../../src/integrations/gitlab/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../capabilities/intake.js';\nimport type { RouteAuth } from '../../routes/route.js';\nimport type { IntegrationStorageHandle } from '../../storage/domains/integrations/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { IssueReconcileWorker } from '../issue-reconcile-worker.js';\nimport { buildGitLabAgentTools } from './agent-tools.js';\nimport {\n GITLAB_ISSUES_PAGE_SIZE,\n GITLAB_NOTES_PAGE_SIZE,\n GITLAB_PROJECTS_PAGE_SIZE,\n GitLabApiClient,\n GitLabApiError,\n} from './api.js';\nimport type { GitLabIssue, GitLabNote, GitLabProject } from './api.js';\nimport { resolveGitLabRules } from './default-rules.js';\nimport type { GitLabEventRules, GitLabRuleOverrides } from './default-rules.js';\nimport { attachGitLabReconciler } from './reconciler.js';\nimport { gitlabReconciliationEnabled, gitlabReconciliationInterval } from './reconciliation-config.js';\nimport { buildGitLabRoutes } from './routes.js';\nimport { attachGitLabRules } from './rules.js';\nimport {\n createGitLabSubscriptionTools,\n parseCreatedMergeRequest,\n subscribeCurrentSessionToMergeRequest,\n} from './session-subscriptions.js';\nimport type { GitLabSubscriptionStorage } from './subscriptions.js';\nimport { buildGitLabVersionControl } from './version-control.js';\n\ninterface GitLabConnectionContext {\n id: string;\n label: string | null;\n api: GitLabApiClient;\n connection: IntegrationConnection;\n host: string;\n webBaseUrl?: string;\n /** Resolves a fresh provider token for a single brokered git operation. */\n repositoryAccessToken?: () => Promise<string>;\n}\n\ninterface GitLabSourceReference {\n /** Present only on legacy v1 ids; authentication is not resource identity. */\n connectionId?: string;\n /** Canonical v2 ids use the GitLab instance host plus immutable project id. */\n host?: string;\n projectId: string;\n /** Legacy ids carry display metadata; v2 resolves it from GitLab. */\n projectPath?: string;\n}\n\ninterface ResolvedGitLabSourceReference extends GitLabSourceReference {\n connectionId: string;\n host: string;\n projectPath: string;\n}\n\ninterface GitLabIssueReference extends GitLabSourceReference {\n issueIid: number;\n}\n\nexport interface GitLabMergeRequestReference {\n version: 1;\n host: string;\n projectId: number;\n mergeRequestIid: number;\n}\n\ninterface GitLabPageCursor {\n source: number;\n page: number;\n}\n\nexport interface GitLabStatusConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\nconst DIRECT_CONNECTION_ID = 'direct';\nconst GITLAB_CONNECTION_TOKEN_PREFIX = 'gitlab-connection:';\nconst GITLAB_SOURCE_PREFIX = 'gitlab-project:';\nconst GITLAB_ISSUE_PREFIX = 'gitlab-issue:';\nconst GITLAB_MERGE_REQUEST_PREFIX = 'gitlab-pr:';\nexport const GITLAB_TRUSTED_ACCESS_LEVEL = 30;\n// Bound issue detail reads to 2,000 notes. GitLab does not expose truncation through the Intake contract.\nconst MAX_NOTES_PAGES = 20;\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport abstract class GitLabIntegrationBase implements FactoryIntegration {\n readonly id = 'gitlab';\n #projects: FactoryProjectsStorage | undefined;\n #sourceControl: SourceControlStorageHandle | undefined;\n #integrationStorage: GitLabSubscriptionStorage | undefined;\n #auth: RouteAuth | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n readonly rules: GitLabEventRules;\n\n constructor(rules?: GitLabRuleOverrides) {\n this.rules = resolveGitLabRules(rules);\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n readonly versionControl = buildGitLabVersionControl({\n contextForConnection: connection => this.#versionControlContext(connection),\n contextForStoredInstallation: (connection, host) => this.#versionControlContextForInstallation(connection, host),\n });\n initialize({\n storage,\n projects,\n auth,\n sourceControl,\n }: {\n storage?: IntegrationStorageHandle;\n projects: FactoryProjectsStorage;\n auth: RouteAuth;\n sourceControl?: SourceControlStorageHandle;\n }): void {\n this.#projects = projects;\n this.#auth = auth;\n this.#sourceControl = sourceControl;\n this.#integrationStorage = storage as GitLabSubscriptionStorage | undefined;\n }\n\n /** Generic integration persistence (merge-request subscriptions). */\n get integrationStorage(): GitLabSubscriptionStorage {\n if (!this.#integrationStorage) {\n throw new Error(`${this.constructor.name} is not initialized — the factory binds storage during prepare().`);\n }\n return this.#integrationStorage;\n }\n\n /** Source-control rows for this integration; absent when the host runs without them. */\n get sourceControlStorage(): SourceControlStorageHandle | undefined {\n return this.#sourceControl;\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) {\n throw new Error(`${this.constructor.name} is not initialized — the factory binds storage during prepare().`);\n }\n return this.#projects;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n const orgId = project?.orgId ?? null;\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n } catch {\n return null;\n }\n }\n\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n }\n\n abstract hasActiveConnections(): Promise<boolean>;\n abstract authFailureMessage(): string;\n protected abstract activeContexts(): Promise<GitLabConnectionContext[]>;\n protected abstract contextById(connectionId: string): Promise<GitLabConnectionContext>;\n\n /** Resolve only a GitLab repository linked to this Factory in this organization. */\n async getLinkedRepository(input: { orgId: string; factoryProjectId: string; projectRepositoryId: string }) {\n if (!this.#sourceControl) return null;\n const connections = await this.#sourceControl.connections.list({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n });\n for (const connection of connections) {\n if (connection.integrationId !== 'gitlab') continue;\n const links = await this.#sourceControl.projectRepositories.list({\n orgId: input.orgId,\n connectionId: connection.id,\n });\n const link = links.find(candidate => candidate.id === input.projectRepositoryId);\n if (!link) continue;\n const repository = await this.#sourceControl.repositories.get({ orgId: input.orgId, id: link.repositoryId });\n const installation = await this.#sourceControl.installations.get({\n orgId: input.orgId,\n id: connection.installationId,\n });\n if (!repository || !installation) return null;\n const host = installation.providerMetadata.host;\n if (typeof host !== 'string' || !host) return null;\n return { repository, host: normalizeGitLabHost(host) };\n }\n return null;\n }\n\n async getProjectMemberAccessLevel(\n connectionId: string,\n projectId: string,\n username: string,\n ): Promise<number | undefined> {\n const normalized = username.trim().toLowerCase();\n if (!normalized) return undefined;\n const members = await (await this.contextById(connectionId)).api.listProjectMembers(projectId, { query: username });\n const member = members.find(\n candidate => candidate.username.toLowerCase() === normalized && candidate.state !== 'blocked',\n );\n return member?.access_level;\n }\n\n async getWorkItemAuthorUsername(\n connectionId: string,\n projectId: string,\n kind: 'issue' | 'merge_request',\n iid: number,\n ): Promise<string | undefined> {\n const api = (await this.contextById(connectionId)).api;\n const item = kind === 'issue' ? await api.getIssue(projectId, iid) : await api.getMergeRequest(projectId, iid);\n return item.author?.username;\n }\n\n async isProjectMemberTrustedForSource(sourceId: string, username: string): Promise<boolean> {\n const reference = decodeSourceId(sourceId);\n if (!reference) throw new GitLabApiError('GitLab source identity is invalid.', 400);\n const { context, source } = await this.#resolveSourceReference(reference);\n const accessLevel = await this.getProjectMemberAccessLevel(context.id, source.projectId, username);\n return (accessLevel ?? 0) >= GITLAB_TRUSTED_ACCESS_LEVEL;\n }\n\n async getIssueForFactoryProject(input: {\n orgId: string;\n factoryProjectId: string;\n issueId: string;\n }): Promise<IntakeIssueDetail | null> {\n if (!this.#sourceControl) throw new GitLabApiError('GitLab source control is unavailable.', 503);\n const reference = decodeIssueReference(input.issueId);\n const locator = parseIssueLocator(input.issueId);\n if (!reference && !locator) {\n throw new GitLabApiError('GitLab issue must include a project and issue IID.', 400);\n }\n\n const normalizePath = (value: string) => value.replace(/^\\/+|\\/+$/g, '').toLowerCase();\n const matches: Array<{\n connectionId: string;\n projectId: string;\n projectPath: string;\n }> = [];\n const connections = await this.#sourceControl.connections.list({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n });\n for (const connection of connections) {\n const installation = await this.#sourceControl.installations.get({\n orgId: input.orgId,\n id: connection.installationId,\n });\n if (!installation) continue;\n for (const projectRepository of await this.#sourceControl.projectRepositories.list({\n orgId: input.orgId,\n connectionId: connection.id,\n })) {\n const repository = await this.#sourceControl.repositories.get({\n orgId: input.orgId,\n id: projectRepository.repositoryId,\n });\n if (!repository) continue;\n if (reference?.projectId && repository.externalId !== reference.projectId) continue;\n if (reference?.projectPath && normalizePath(repository.slug) !== normalizePath(reference.projectPath)) continue;\n if (locator?.projectPath && normalizePath(repository.slug) !== normalizePath(locator.projectPath)) continue;\n if (reference?.connectionId && installation.externalId !== reference.connectionId) continue;\n const installationHost =\n typeof installation.providerMetadata.host === 'string'\n ? normalizeGitLabHost(installation.providerMetadata.host)\n : null;\n if (reference?.host && installationHost !== normalizeGitLabHost(reference.host)) continue;\n if (locator?.host && installationHost !== normalizeGitLabHost(locator.host)) continue;\n matches.push({\n connectionId: installation.externalId,\n projectId: repository.externalId,\n projectPath: repository.slug,\n });\n }\n }\n const match = matches.sort((left, right) => left.connectionId.localeCompare(right.connectionId))[0];\n if (!match) throw new GitLabApiError('GitLab issue is outside the active Factory project.', 404);\n return this.intake.getIssue({\n connection: gitlabConnection(match.connectionId),\n sourceId: encodeSourceId(match),\n issueId: input.issueId,\n });\n }\n\n /** Platform mode exposes org connections; direct mode returns undefined. */\n async statusConnections(): Promise<GitLabStatusConnection[] | undefined> {\n return undefined;\n }\n\n /** Direct mode verifies its configured credential; Platform mode uses connection status. */\n async verifyStatus(): Promise<void> {\n // Platform connection state is authoritative unless a provider overrides this probe.\n }\n\n protected get webhookSecret(): string | undefined {\n return undefined;\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!gitlabReconciliationEnabled()) return [];\n const reconcile = attachGitLabReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = gitlabReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n routes(ctx: IntegrationContext): ApiRoute[] {\n const ingestFactoryEvent = attachGitLabRules(this, ctx);\n return buildGitLabRoutes({\n gitlab: this,\n auth: ctx.auth,\n intake: ctx.storage?.intake,\n emitAudit: ctx.hooks?.emitAudit,\n sandbox: ctx.sandbox,\n webhookSecret: this.webhookSecret,\n ingestFactoryEvent,\n ...(ctx.controller ? { controller: ctx.controller } : {}),\n });\n }\n\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildGitLabAgentTools({ requestContext: args.requestContext, gitlab: this });\n }\n\n sessionTools({ requestContext }: { requestContext: RequestContext }): IntegrationTools {\n return createGitLabSubscriptionTools(requestContext, this);\n }\n\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const mergeRequestUrl = parseCreatedMergeRequest(toolContext);\n if (!mergeRequestUrl || !requestContext) return;\n await subscribeCurrentSessionToMergeRequest(requestContext, mergeRequestUrl, 'auto-create-change-request', this);\n }\n\n abstract diagnostics(): Record<string, unknown>;\n\n /** Prefer the configured direct token over an older Platform installation on the same host. */\n async resolveActiveConnectionForHost(storedConnectionId: string, host: string): Promise<string> {\n if (this.diagnostics().mode !== 'direct') return storedConnectionId;\n const [direct] = await this.activeContexts();\n return direct && normalizeGitLabHost(direct.host) === normalizeGitLabHost(host) ? direct.id : storedConnectionId;\n }\n\n async #versionControlContext(connection: IntegrationConnection): Promise<GitLabConnectionContext> {\n const connectionId = connectionIdFromConnection(connection);\n if (connectionId) return this.contextById(connectionId);\n const contexts = await this.activeContexts();\n if (contexts.length !== 1) {\n throw new GitLabApiError(\n 'GitLab version-control requests must identify a connection when multiple GitLab accounts are connected.',\n 400,\n );\n }\n return contexts[0]!;\n }\n async #versionControlContextForInstallation(\n connection: IntegrationConnection,\n host: string | undefined,\n ): Promise<GitLabConnectionContext> {\n // A direct PAT intentionally overrides Platform credentials. Old links can\n // retain their Platform connection id; use the PAT only for the same host.\n if (this.diagnostics().mode === 'direct' && host) {\n const [direct] = await this.activeContexts();\n if (direct && normalizeGitLabHost(host) === normalizeGitLabHost(direct.host)) return direct;\n }\n return this.#versionControlContext(connection);\n }\n async #contextForReference(reference: GitLabSourceReference): Promise<GitLabConnectionContext> {\n if (reference.connectionId) return this.contextById(reference.connectionId);\n const contexts = (await this.activeContexts()).filter(\n context => !reference.host || normalizeGitLabHost(context.host) === normalizeGitLabHost(reference.host),\n );\n if (contexts.length === 0) {\n throw new GitLabApiError('No active GitLab connection can access this source.', 401);\n }\n if (contexts.length === 1) return contexts[0]!;\n\n if (this.#sourceControl) {\n const linkedConnectionIds = new Set(\n (await this.#sourceControl.projectRepositories.listConfiguredExternalKeys())\n .filter(key => key.repositoryExternalId === reference.projectId)\n .map(key => key.installationExternalId),\n );\n const linked = contexts.filter(context => linkedConnectionIds.has(context.id));\n if (linked.length > 0) return linked.sort((left, right) => left.id.localeCompare(right.id))[0]!;\n }\n\n for (const context of contexts.sort((left, right) => left.id.localeCompare(right.id))) {\n try {\n await context.api.getProject(reference.projectId);\n return context;\n } catch (error) {\n if (!(error instanceof GitLabApiError) || ![401, 403, 404].includes(error.status ?? 0)) throw error;\n // This credential cannot access the project; try another active account for the same canonical source.\n }\n }\n throw new GitLabApiError('No active GitLab connection can access this source.', 404);\n }\n\n async #resolveSourceReference(\n reference: GitLabSourceReference,\n ): Promise<{ context: GitLabConnectionContext; source: ResolvedGitLabSourceReference }> {\n const context = await this.#contextForReference(reference);\n const projectPath =\n reference.projectPath ?? (await context.api.getProject(reference.projectId)).path_with_namespace;\n return {\n context,\n source: {\n connectionId: context.id,\n host: normalizeGitLabHost(reference.host ?? context.host),\n projectId: reference.projectId,\n projectPath,\n },\n };\n }\n\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n const reference = decodeIssueReference(externalSource.externalId);\n if (!reference) return null;\n const { context, source } = await this.#resolveSourceReference(reference);\n return {\n connection: context.connection,\n sourceId: encodeSourceId(source),\n issueId: String(reference.issueIid),\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const sources = new Map<string, IntakeSource>();\n for (const context of await this.activeContexts()) {\n for (let page = 1; ; page++) {\n const projects = await context.api.listProjects({ page });\n for (const project of projects) {\n const source = this.#toIntakeSource(context, project);\n if (!sources.has(source.id)) sources.set(source.id, source);\n }\n if (projects.length < GITLAB_PROJECTS_PAGE_SIZE) break;\n }\n }\n return [...sources.values()];\n }\n\n async #listItems(input: ListIntakeItemsInput): Promise<IntakeItemPage> {\n const result = await this.#listIssuePage(input.sourceIds, input.cursor);\n return {\n items: result.issues.map(({ issue, source, context }) => ({\n source: {\n type: 'issue',\n externalId: encodeIssueReference({ ...source, issueIid: issue.iid }),\n url: issue.web_url,\n },\n sourceId: encodeSourceId(source),\n title: `${source.projectPath}#${issue.iid}: ${issue.title}`,\n status: issue.state,\n labels: issue.labels ?? [],\n assignee: displayName(issue.assignee),\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n metadata: {\n identifier: `${source.projectPath}#${issue.iid}`,\n projectId: source.projectId,\n projectPath: source.projectPath,\n connectionId: context.id,\n accountLabel: context.label,\n labelColors: Object.fromEntries((issue.labelDetails ?? []).map(label => [label.name, label.color])),\n },\n })),\n nextCursor: result.nextCursor,\n };\n }\n\n async #listIssues(input: ListIntakeIssuesInput) {\n const result = await this.#listIssuePage(input.sourceIds, input.cursor, input.labels);\n return {\n issues: result.issues.map(({ issue, source }) => ({\n ...this.#toIntakeIssue(issue, source.projectPath),\n sourceId: encodeSourceId(source),\n })),\n nextCursor: result.nextCursor,\n };\n }\n\n async #listIssuePage(sourceIds: string[], cursor?: string, labels?: string[]) {\n const sources = sourceIds.map(decodeSourceId).filter((source): source is GitLabSourceReference => source !== null);\n if (sources.length === 0) return { issues: [], nextCursor: null };\n const current = decodePageCursor(cursor);\n if (!current || current.source >= sources.length) return { issues: [], nextCursor: null };\n\n for (let sourceIndex = current.source; sourceIndex < sources.length; sourceIndex++) {\n const { source, context } = await this.#resolveSourceReference(sources[sourceIndex]!);\n const page = sourceIndex === current.source ? current.page : 1;\n const issues = await context.api.listIssues(source.projectId, { page, labels });\n const nextCursor =\n issues.length === GITLAB_ISSUES_PAGE_SIZE\n ? encodePageCursor({ source: sourceIndex, page: page + 1 })\n : sourceIndex + 1 < sources.length\n ? encodePageCursor({ source: sourceIndex + 1, page: 1 })\n : null;\n return { issues: issues.map(issue => ({ issue, source, context })), nextCursor };\n }\n return { issues: [], nextCursor: null };\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: GitLabIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n const notes = await this.#listAllNotes(resolved.context.api, resolved.projectId, resolved.issueIid);\n return {\n ...this.#toIntakeIssue(issue, resolved.projectPath),\n description: issue.description?.trim() || null,\n comments: notes\n .filter(note => !note.system)\n .map(note => ({ author: displayName(note.author), body: note.body, createdAt: note.created_at })),\n };\n }\n\n async #listAllNotes(api: GitLabApiClient, projectId: string, issueIid: number): Promise<GitLabNote[]> {\n const notes: GitLabNote[] = [];\n for (let page = 1; page <= MAX_NOTES_PAGES; page++) {\n const result = await api.listNotes(projectId, issueIid, { page });\n notes.push(...result);\n if (result.length < GITLAB_NOTES_PAGE_SIZE) break;\n }\n return notes;\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#resolveRequest(input);\n try {\n const issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n const note = await resolved.context.api.createNote(resolved.projectId, resolved.issueIid, input.body);\n return { id: String(note.id), url: `${issue.web_url}#note_${note.id}` };\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: GitLabIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n const stateEvent = targetStateEvent(input);\n if (!stateEvent) return null;\n if ((stateEvent === 'close' && issue.state === 'closed') || (stateEvent === 'reopen' && issue.state === 'opened')) {\n return this.#toIntakeIssue(issue, resolved.projectPath);\n }\n const updated = await resolved.context.api.updateIssueState(resolved.projectId, resolved.issueIid, stateEvent);\n return this.#toIntakeIssue(updated, resolved.projectPath);\n }\n\n async #resolveRequest(input: GetIntakeIssueInput): Promise<{\n context: GitLabConnectionContext;\n projectId: string;\n projectPath: string;\n issueIid: number;\n }> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const locator = parseIssueLocator(input.issueId);\n const reference = issueReference ?? source;\n let context: GitLabConnectionContext;\n if (reference) {\n context = await this.#contextForReference(reference);\n } else {\n const connectionId = connectionIdFromConnection(input.connection);\n const contexts = connectionId ? [await this.contextById(connectionId)] : await this.activeContexts();\n if (contexts.length !== 1) {\n throw new GitLabApiError('GitLab issue reference must resolve to exactly one active connection.', 400);\n }\n context = contexts[0]!;\n }\n const projectId = issueReference?.projectId ?? source?.projectId ?? locator?.projectPath;\n const issueIid = issueReference?.issueIid ?? locator?.issueIid ?? parsePositiveInteger(input.issueId);\n if (!projectId || !issueIid) {\n throw new GitLabApiError(\n 'GitLab issue must include a project and issue IID (for example, group/project#42).',\n 400,\n );\n }\n const projectPath =\n issueReference?.projectPath ??\n source?.projectPath ??\n locator?.projectPath ??\n (await context.api.getProject(projectId)).path_with_namespace;\n return { context, projectId, projectPath, issueIid };\n }\n\n #toIntakeSource(context: GitLabConnectionContext, project: GitLabProject): IntakeSource {\n const reference: GitLabSourceReference = {\n host: normalizeGitLabHost(context.host),\n projectId: String(project.id),\n projectPath: project.path_with_namespace,\n };\n return {\n id: encodeSourceId(reference),\n name: project.path_with_namespace,\n type: 'project',\n metadata: {\n projectId: String(project.id),\n projectPath: project.path_with_namespace,\n defaultBranch: project.default_branch ?? null,\n connectionId: context.id,\n accountLabel: context.label,\n url: project.web_url,\n },\n };\n }\n\n #toIntakeIssue(issue: GitLabIssue, projectPath: string): IntakeIssue {\n const assignees = issue.assignees?.map(displayName).filter((name): name is string => name !== null) ?? [];\n return {\n id: String(issue.iid),\n identifier: `${projectPath}#${issue.iid}`,\n title: issue.title,\n url: issue.web_url,\n author: displayName(issue.author),\n authorUsername: issue.author?.username ?? null,\n state: issue.state,\n stateType: issue.state === 'closed' ? 'completed' : 'unstarted',\n priority: typeof issue.weight === 'number' ? String(issue.weight) : null,\n assignee: displayName(issue.assignee) ?? assignees[0] ?? null,\n assignees,\n source: projectPath,\n labels: issue.labels ?? [],\n labelColors: Object.fromEntries((issue.labelDetails ?? []).map(label => [label.name, label.color])),\n commentCount: issue.user_notes_count ?? null,\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n };\n }\n}\n\nexport type GitLabAccessTokenType = 'personal' | 'group';\n\nexport interface GitLabIntegrationConfig {\n rules?: GitLabRuleOverrides;\n accessToken?: string;\n /** Defaults to `personal`; both token types authenticate identically but have different resource reach. */\n accessTokenType?: GitLabAccessTokenType;\n baseUrl?: string;\n webhookSecret?: string;\n fetchImpl?: typeof fetch;\n}\n\nconst DIRECT_CONNECTION_TOKEN = 'gitlab-direct-access-token';\n\nexport class GitLabIntegration extends GitLabIntegrationBase {\n readonly #accessToken: string;\n readonly #accessTokenType: GitLabAccessTokenType;\n readonly #baseUrl: string;\n readonly #webhookSecret: string | undefined;\n readonly #context: GitLabConnectionContext;\n\n constructor(config: GitLabIntegrationConfig = {}) {\n super(config.rules);\n const accessToken = config.accessToken?.trim() || process.env.GITLAB_ACCESS_TOKEN?.trim();\n if (!accessToken) {\n throw new Error('GitLabIntegration: missing required GITLAB_ACCESS_TOKEN.');\n }\n this.#accessToken = accessToken;\n this.#accessTokenType = parseAccessTokenType(\n config.accessTokenType ?? process.env.GITLAB_ACCESS_TOKEN_TYPE?.trim(),\n );\n this.#baseUrl = (config.baseUrl ?? process.env.GITLAB_BASE_URL?.trim() ?? 'https://gitlab.com').replace(/\\/+$/, '');\n this.#webhookSecret = config.webhookSecret?.trim() || process.env.GITLAB_WEBHOOK_SECRET?.trim() || undefined;\n const api = new GitLabApiClient({\n baseUrl: this.#baseUrl,\n accessToken: this.#accessToken,\n ...(config.fetchImpl ? { fetchImpl: config.fetchImpl } : {}),\n });\n this.#context = {\n id: DIRECT_CONNECTION_ID,\n label: new URL(this.#baseUrl).host,\n api,\n connection: { type: 'oauth', accessToken: DIRECT_CONNECTION_TOKEN },\n host: new URL(this.#baseUrl).host,\n webBaseUrl: this.#baseUrl,\n repositoryAccessToken: async () => this.#accessToken,\n };\n }\n\n protected override get webhookSecret(): string | undefined {\n return this.#webhookSecret;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return true;\n }\n\n override async verifyStatus(): Promise<void> {\n await this.#context.api.getCurrentUser();\n }\n\n authFailureMessage(): string {\n return `GitLab rejected the configured ${this.#accessTokenType} access token. Check the token and its scopes.`;\n }\n\n protected async activeContexts(): Promise<GitLabConnectionContext[]> {\n return [this.#context];\n }\n\n protected async contextById(connectionId: string): Promise<GitLabConnectionContext> {\n if (connectionId !== DIRECT_CONNECTION_ID) {\n throw new GitLabApiError('GitLab connection is unavailable.', 401);\n }\n return this.#context;\n }\n\n diagnostics(): Record<string, unknown> {\n return {\n configured: true,\n mode: 'direct',\n accessTokenType: this.#accessTokenType,\n endpointHost: new URL(this.#baseUrl).host,\n webhookConfigured: Boolean(this.#webhookSecret),\n };\n }\n}\n\nfunction parseAccessTokenType(value: string | undefined): GitLabAccessTokenType {\n const normalized = value?.trim().toLowerCase() || 'personal';\n if (normalized === 'personal' || normalized === 'group') return normalized;\n throw new Error(\"GitLabIntegration: GITLAB_ACCESS_TOKEN_TYPE must be 'personal' or 'group'.\");\n}\n\nexport function encodeSourceId(reference: GitLabSourceReference): string {\n if (reference.host) {\n return `${GITLAB_SOURCE_PREFIX}${encodeOpaque({\n version: 2,\n host: normalizeGitLabHost(reference.host),\n projectId: reference.projectId,\n })}`;\n }\n if (!reference.connectionId || !reference.projectPath) {\n throw new Error('GitLab source reference is missing its instance host.');\n }\n return `${GITLAB_SOURCE_PREFIX}${encodeOpaque(reference)}`;\n}\n\nexport function decodeSourceId(value: string): GitLabSourceReference | null {\n return decodeOpaque(value, GITLAB_SOURCE_PREFIX, isSourceReference);\n}\n\nexport function encodeIssueReference(reference: GitLabIssueReference): string {\n if (reference.host) {\n return `${GITLAB_ISSUE_PREFIX}${encodeOpaque({\n version: 2,\n host: normalizeGitLabHost(reference.host),\n projectId: reference.projectId,\n issueIid: reference.issueIid,\n })}`;\n }\n if (!reference.connectionId || !reference.projectPath) {\n throw new Error('GitLab issue reference is missing its instance host.');\n }\n return `${GITLAB_ISSUE_PREFIX}${encodeOpaque(reference)}`;\n}\n\nexport function decodeIssueReference(value: string): GitLabIssueReference | null {\n return decodeOpaque(value, GITLAB_ISSUE_PREFIX, isIssueReference);\n}\n\nexport function decodeMergeRequestReference(value: string): GitLabMergeRequestReference | null {\n return decodeOpaque(value, GITLAB_MERGE_REQUEST_PREFIX, isMergeRequestReference);\n}\n\nexport function gitlabConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${GITLAB_CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(GITLAB_CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(GITLAB_CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\nfunction encodeOpaque(value: unknown): string {\n return Buffer.from(JSON.stringify(value)).toString('base64url');\n}\n\nfunction decodeOpaque<T>(value: string, prefix: string, guard: (value: unknown) => value is T): T | null {\n if (!value.startsWith(prefix)) return null;\n try {\n const decoded = JSON.parse(Buffer.from(value.slice(prefix.length), 'base64url').toString('utf8')) as unknown;\n return guard(decoded) ? decoded : null;\n } catch {\n return null;\n }\n}\n\nexport function normalizeGitLabHost(host: string): string {\n return host.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nfunction isSourceReference(value: unknown): value is GitLabSourceReference {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const ref = value as Record<string, unknown>;\n if (typeof ref.projectId !== 'string' || ref.projectId.length === 0) return false;\n const canonical = ref.version === 2 && typeof ref.host === 'string' && normalizeGitLabHost(ref.host).length > 0;\n const legacy =\n typeof ref.connectionId === 'string' &&\n ref.connectionId.length > 0 &&\n typeof ref.projectPath === 'string' &&\n ref.projectPath.length > 0;\n return canonical || legacy;\n}\n\nfunction isIssueReference(value: unknown): value is GitLabIssueReference {\n return (\n isSourceReference(value) &&\n Number.isSafeInteger((value as GitLabIssueReference).issueIid) &&\n (value as GitLabIssueReference).issueIid > 0\n );\n}\n\nfunction isMergeRequestReference(value: unknown): value is GitLabMergeRequestReference {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const ref = value as Record<string, unknown>;\n return (\n ref.version === 1 &&\n typeof ref.host === 'string' &&\n normalizeGitLabHost(ref.host).length > 0 &&\n Number.isSafeInteger(ref.projectId) &&\n (ref.projectId as number) > 0 &&\n Number.isSafeInteger(ref.mergeRequestIid) &&\n (ref.mergeRequestIid as number) > 0\n );\n}\n\nfunction encodePageCursor(cursor: GitLabPageCursor): string {\n return encodeOpaque(cursor);\n}\n\nfunction decodePageCursor(value: string | undefined): GitLabPageCursor | null {\n if (!value) return { source: 0, page: 1 };\n try {\n const decoded = JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as Record<string, unknown>;\n if (!Number.isSafeInteger(decoded.source) || Number(decoded.source) < 0) return null;\n if (!Number.isSafeInteger(decoded.page) || Number(decoded.page) < 1) return null;\n return { source: Number(decoded.source), page: Number(decoded.page) };\n } catch {\n return null;\n }\n}\n\nfunction displayName(user: { name?: string | null; username: string } | null | undefined): string | null {\n return user?.name?.trim() || user?.username || null;\n}\n\nfunction parsePositiveInteger(value: string): number | null {\n if (!/^\\d+$/.test(value)) return null;\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction parseIssueLocator(value: string): {\n host?: string;\n projectPath: string;\n issueIid: number;\n} | null {\n const shorthand = value.match(/^(.+?)#(\\d+)$/);\n if (shorthand) {\n const issueIid = parsePositiveInteger(shorthand[2]!);\n const projectPath = shorthand[1]!.replace(/^\\/+|\\/+$/g, '');\n return issueIid && projectPath ? { projectPath, issueIid } : null;\n }\n try {\n const url = new URL(value);\n const match = url.pathname.match(/^\\/(.+)\\/-\\/issues\\/(\\d+)\\/?$/);\n if (!match) return null;\n const issueIid = parsePositiveInteger(match[2]!);\n if (!issueIid) return null;\n const projectPath = decodeURIComponent(match[1]!);\n return { host: url.host, projectPath, issueIid };\n } catch {\n return null;\n }\n}\n\nfunction targetStateEvent(input: UpdateIntakeIssueInput): 'close' | 'reopen' | null {\n if (input.state.kind === 'byType') {\n // GitLab issues have only opened/closed states, so canceled is intentionally mapped to close.\n return input.state.stateType === 'completed' || input.state.stateType === 'canceled' ? 'close' : 'reopen';\n }\n const name = input.state.name.trim().toLowerCase();\n if (name === 'closed' || name === 'close') return 'close';\n if (name === 'opened' || name === 'open' || name === 'reopen') return 'reopen';\n return null;\n}\n"],"mappings":"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"}
@@ -6,7 +6,8 @@ function configuredValue(primary, legacy) {
6
6
  function gitlabReconciliationEnabled() {
7
7
  const primary = process.env.MASTRACODE_GITLAB_RECONCILE_ENABLED;
8
8
  const value = configuredValue(primary, process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED);
9
- return optionalBoolean(primary?.trim() ? "MASTRACODE_GITLAB_RECONCILE_ENABLED" : "MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED", value, "gitlab") ?? true;
9
+ const name = primary?.trim() ? "MASTRACODE_GITLAB_RECONCILE_ENABLED" : "MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED";
10
+ return optionalBoolean(name, value, "gitlab") ?? true;
10
11
  }
11
12
  function gitlabReconciliationInterval() {
12
13
  return optionalPositiveInteger(configuredValue(process.env.MASTRACODE_GITLAB_RECONCILE_INTERVAL_MS, process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_INTERVAL_MS));
@@ -1 +1 @@
1
- {"version":3,"file":"reconciliation-config.js","names":[],"sources":["../../../src/integrations/gitlab/reconciliation-config.ts"],"sourcesContent":["import { optionalBoolean, optionalPositiveInteger } from '../reconciliation-config.js';\n\nfunction configuredValue(primary: string | undefined, legacy: string | undefined): string | undefined {\n return primary?.trim() ? primary : legacy;\n}\n\nexport function gitlabReconciliationEnabled(): boolean {\n const primary = process.env.MASTRACODE_GITLAB_RECONCILE_ENABLED;\n const value = configuredValue(primary, process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED);\n const name = primary?.trim() ? 'MASTRACODE_GITLAB_RECONCILE_ENABLED' : 'MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED';\n return optionalBoolean(name, value, 'gitlab') ?? true;\n}\n\nexport function gitlabReconciliationInterval(): number | undefined {\n return optionalPositiveInteger(\n configuredValue(\n process.env.MASTRACODE_GITLAB_RECONCILE_INTERVAL_MS,\n process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_INTERVAL_MS,\n ),\n );\n}\n"],"mappings":";;AAEA,SAAS,gBAAgB,SAA6B,QAAgD;CACpG,OAAO,SAAS,KAAK,IAAI,UAAU;AACrC;AAEA,SAAgB,8BAAuC;CACrD,MAAM,UAAU,QAAQ,IAAI;CAC5B,MAAM,QAAQ,gBAAgB,SAAS,QAAQ,IAAI,yCAAyC;CAE5F,OAAO,gBADM,SAAS,KAAK,IAAI,wCAAwC,6CAC1C,OAAO,QAAQ,KAAK;AACnD;AAEA,SAAgB,+BAAmD;CACjE,OAAO,wBACL,gBACE,QAAQ,IAAI,yCACZ,QAAQ,IAAI,6CACd,CACF;AACF"}
1
+ {"version":3,"file":"reconciliation-config.js","names":[],"sources":["../../../src/integrations/gitlab/reconciliation-config.ts"],"sourcesContent":["import { optionalBoolean, optionalPositiveInteger } from '../reconciliation-config.js';\n\nfunction configuredValue(primary: string | undefined, legacy: string | undefined): string | undefined {\n return primary?.trim() ? primary : legacy;\n}\n\nexport function gitlabReconciliationEnabled(): boolean {\n const primary = process.env.MASTRACODE_GITLAB_RECONCILE_ENABLED;\n const value = configuredValue(primary, process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED);\n const name = primary?.trim() ? 'MASTRACODE_GITLAB_RECONCILE_ENABLED' : 'MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED';\n return optionalBoolean(name, value, 'gitlab') ?? true;\n}\n\nexport function gitlabReconciliationInterval(): number | undefined {\n return optionalPositiveInteger(\n configuredValue(\n process.env.MASTRACODE_GITLAB_RECONCILE_INTERVAL_MS,\n process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_INTERVAL_MS,\n ),\n );\n}\n"],"mappings":";;AAEA,SAAS,gBAAgB,SAA6B,QAAgD;CACpG,OAAO,SAAS,KAAK,IAAI,UAAU;AACrC;AAEA,SAAgB,8BAAuC;CACrD,MAAM,UAAU,QAAQ,IAAI;CAC5B,MAAM,QAAQ,gBAAgB,SAAS,QAAQ,IAAI,yCAAyC;CAC5F,MAAM,OAAO,SAAS,KAAK,IAAI,wCAAwC;CACvE,OAAO,gBAAgB,MAAM,OAAO,QAAQ,KAAK;AACnD;AAEA,SAAgB,+BAAmD;CACjE,OAAO,wBACL,gBACE,QAAQ,IAAI,yCACZ,QAAQ,IAAI,6CACd,CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","names":["#targets","#ingestProject","#trusted","#evaluate","#trackOpenMergeRequest"],"sources":["../../../src/integrations/gitlab/rules.ts"],"sourcesContent":["import {\n boardEventHandlers,\n boardForWorkItem,\n eventBoardId,\n rulePhaseField,\n withBoardClosure,\n} from '../../boards/index.js';\nimport type { BoardRegistry } from '../../boards/index.js';\nimport { hasExplicitRuleOverride, selectEventRule } from '../../rules/event-rules.js';\nimport type {\n FactoryGitLabEventName,\n FactoryGitLabRuleContext,\n FactoryRuleActor,\n FactoryRuleDecision,\n} from '../../rules/types.js';\nimport { assertFactoryDecisionTarget, validateFactoryRuleDecisions } from '../../rules/validation.js';\nimport type { IntakeStorage } from '../../storage/domains/intake/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type {\n ExternalRepositoryProjectTarget,\n SourceControlStorageHandle,\n} from '../../storage/domains/source-control/base.js';\nimport { WorkItemUpdateConflictError } from '../../storage/domains/work-items/base.js';\nimport type { WorkItemRow, WorkItemsStorage } from '../../storage/domains/work-items/base.js';\nimport type { IntegrationContext } from '../base.js';\nimport { recordPullRequestHead } from '../pull-request-head.js';\nimport type { GitLabEventRules } from './default-rules.js';\nimport { encodeIssueReference, encodeSourceId, GITLAB_TRUSTED_ACCESS_LEVEL } from './integration.js';\nimport type { ParsedGitLabWebhook } from './webhook.js';\n\nconst RULE_TIMEOUT_MS = 5_000;\n\ntype IngressStatus = 'ignored' | 'committed' | 'replayed' | 'missing';\n\nfunction object(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;\n}\n\nfunction string(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction number(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\n/** GitLab sends `2024-01-02T03:04:05Z`, `2024-01-02 03:04:05 UTC` or `2024-01-02 03:04:05 +0000`. */\nfunction timestamp(value: unknown): number | undefined {\n const text = string(value);\n if (!text) return undefined;\n const spaced = /^(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?) (?:UTC|([+-]\\d{2}):?(\\d{2}))$/.exec(text);\n const parsed = Date.parse(\n spaced ? `${spaced[1]}T${spaced[2]}${spaced[3] ? `${spaced[3]}:${spaced[4]}` : 'Z'}` : text,\n );\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction boolean(value: unknown): boolean | undefined {\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction usernames(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.flatMap(entry => {\n const username = string(object(entry)?.username);\n return username ? [username] : [];\n });\n}\n\nfunction labelNames(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.flatMap(entry => {\n if (typeof entry === 'string') return entry ? [entry] : [];\n const title = string(object(entry)?.title) ?? string(object(entry)?.name);\n return title ? [title] : [];\n });\n}\n\nfunction labelColors(value: unknown): Record<string, string> {\n if (!Array.isArray(value)) return {};\n return Object.fromEntries(\n value.flatMap(entry => {\n const label = object(entry);\n const name = string(label?.title) ?? string(label?.name);\n const color = string(label?.color);\n return name && color ? [[name, color]] : [];\n }),\n );\n}\n\nfunction eventName(parsed: ParsedGitLabWebhook): FactoryGitLabEventName | undefined {\n const attributes = object(parsed.payload.object_attributes);\n const action = string(attributes?.action)?.toLowerCase();\n if (parsed.event === 'Issue Hook') {\n if (action === 'open' || action === 'reopen') return 'issueOpened';\n if (action === 'close') return 'issueClosed';\n return 'issueUpdated';\n }\n if (parsed.event === 'Note Hook') {\n const noteableType = string(attributes?.noteable_type);\n if (noteableType === 'Issue') return 'issueCommented';\n if (noteableType === 'MergeRequest') return 'mergeRequestCommented';\n return;\n }\n if (parsed.event !== 'Merge Request Hook') return;\n if (action === 'open' || action === 'reopen') return 'mergeRequestOpened';\n if (action === 'merge') return 'mergeRequestMerged';\n if (action === 'close') return 'mergeRequestClosed';\n return 'mergeRequestUpdated';\n}\n\nfunction normalizeHost(host: string): string {\n return host.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nfunction hostFromPayload(parsed: ParsedGitLabWebhook): string | undefined {\n const declaredHost = parsed.instanceHost ? normalizeHost(parsed.instanceHost) : undefined;\n const projectUrl = string(object(parsed.payload.project)?.web_url);\n if (!projectUrl) return declaredHost;\n try {\n const payloadHost = normalizeHost(new URL(projectUrl).host);\n if (declaredHost && declaredHost !== payloadHost) return undefined;\n return payloadHost;\n } catch {\n return undefined;\n }\n}\n\nfunction mergeRequestSourceKey(host: string, projectId: number, mergeRequestIid: number): string {\n return `gitlab-pr:${Buffer.from(JSON.stringify({ version: 1, host, projectId, mergeRequestIid }), 'utf8').toString('base64url')}`;\n}\n\nasync function withRuleTimeout<T>(promise: Promise<T>): Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(new Error('FACTORY_RULE_TIMEOUT')), RULE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timeout) clearTimeout(timeout);\n }\n}\n\nconst MAX_STALE_RULE_ATTEMPTS = 3;\n\nexport interface GitLabRulesIntegration {\n readonly rules: GitLabEventRules;\n resolveActiveConnectionForHost?(storedConnectionId: string, host: string): Promise<string>;\n getProjectMemberAccessLevel(connectionId: string, projectId: string, username: string): Promise<number | undefined>;\n getWorkItemAuthorUsername(\n connectionId: string,\n projectId: string,\n kind: 'issue' | 'merge_request',\n iid: number,\n ): Promise<string | undefined>;\n}\n\ninterface GitLabRulesOptions {\n gitlab: GitLabRulesIntegration;\n sourceControl: SourceControlStorageHandle;\n projects: FactoryProjectsStorage;\n storage: WorkItemsStorage;\n intake: IntakeStorage;\n configVersion: string;\n boards: BoardRegistry;\n}\n\ninterface ProjectTarget {\n target: ExternalRepositoryProjectTarget;\n connectionIds: string[];\n}\n\nexport class GitLabRules {\n constructor(private readonly options: GitLabRulesOptions) {}\n\n async ingest(parsed: ParsedGitLabWebhook): Promise<{ status: IngressStatus }> {\n const event = eventName(parsed);\n const project = object(parsed.payload.project);\n const projectId = number(project?.id);\n const projectPath = string(project?.path_with_namespace);\n const host = hostFromPayload(parsed);\n const username = string(parsed.payload.user_username) ?? string(object(parsed.payload.user)?.username);\n if (!event || !projectId || !projectPath || !host || !username) return { status: 'ignored' };\n\n const targets = await this.#targets(String(projectId), host);\n if (targets.length === 0) return { status: 'ignored' };\n const results: Array<{ status: IngressStatus }> = [];\n for (const target of targets) {\n results.push(await this.#ingestProject(parsed, event, projectId, projectPath, host, username, target));\n }\n if (results.some(result => result.status === 'committed')) return { status: 'committed' };\n if (results.some(result => result.status === 'replayed')) return { status: 'replayed' };\n return results[0] ?? { status: 'ignored' };\n }\n\n async #targets(repositoryExternalId: string, host: string): Promise<ProjectTarget[]> {\n const grouped = new Map<string, ProjectTarget>();\n const keys = (await this.options.sourceControl.projectRepositories.listConfiguredExternalKeys()).filter(\n key => key.repositoryExternalId === repositoryExternalId,\n );\n for (const key of keys) {\n const targets = await this.options.sourceControl.projectRepositories.listByExternalRepository({\n installationExternalId: key.installationExternalId,\n repositoryExternalId,\n });\n for (const target of targets) {\n const installation = await this.options.sourceControl.installations.findByExternalId({\n orgId: target.orgId,\n externalId: key.installationExternalId,\n });\n const installationHost = string(installation?.providerMetadata.host);\n if (!installationHost || normalizeHost(installationHost) !== normalizeHost(host)) continue;\n const id = `${target.orgId}:${target.factoryProjectId}`;\n const current = grouped.get(id);\n if (current) {\n if (!current.connectionIds.includes(key.installationExternalId)) {\n current.connectionIds.push(key.installationExternalId);\n }\n } else {\n grouped.set(id, { target, connectionIds: [key.installationExternalId] });\n }\n }\n }\n return [...grouped.values()];\n }\n\n async #ingestProject(\n parsed: ParsedGitLabWebhook,\n event: FactoryGitLabEventName,\n projectId: number,\n projectPath: string,\n host: string,\n username: string,\n target: ProjectTarget,\n ): Promise<{ status: IngressStatus }> {\n const factoryProject = await this.options.projects.get({\n orgId: target.target.orgId,\n id: target.target.factoryProjectId,\n });\n if (!factoryProject) return { status: 'missing' };\n\n const sourceId = encodeSourceId({ host, projectId: String(projectId) });\n const config = await this.options.intake.getConfig({\n orgId: target.target.orgId,\n integrationIds: ['gitlab'],\n });\n if (!config.gitlab?.enabled || !config.gitlab.sourceIds?.includes(sourceId)) return { status: 'ignored' };\n const binding = (\n await this.options.intake.listBindings({ orgId: target.target.orgId, integrationId: 'gitlab' })\n ).find(\n candidate => candidate.sourceId === sourceId && candidate.factoryProjectId === target.target.factoryProjectId,\n );\n if (!binding) return { status: 'ignored' };\n const board = this.options.boards.get(binding.board ?? 'work');\n if (!board) return { status: 'ignored' };\n\n const connectionIds = [\n ...new Set(\n await Promise.all(\n target.connectionIds.map(\n connectionId => this.options.gitlab.resolveActiveConnectionForHost?.(connectionId, host) ?? connectionId,\n ),\n ),\n ),\n ];\n\n const attributes = object(parsed.payload.object_attributes);\n const issue = parsed.event === 'Note Hook' ? object(parsed.payload.issue) : attributes;\n const mergeRequest = parsed.event === 'Note Hook' ? object(parsed.payload.merge_request) : attributes;\n const issueIid = number(issue?.iid);\n const mergeRequestIid = number(mergeRequest?.iid);\n const issueSourceKey = issueIid\n ? encodeIssueReference({ host, projectId: String(projectId), issueIid })\n : undefined;\n const mergeRequestKey = mergeRequestIid ? mergeRequestSourceKey(host, projectId, mergeRequestIid) : undefined;\n const items = await this.options.storage.list({\n orgId: target.target.orgId,\n factoryProjectId: target.target.factoryProjectId,\n });\n const issueItem = issueSourceKey\n ? items.find(\n item => item.externalSource?.integrationId === 'gitlab' && item.externalSource.externalId === issueSourceKey,\n )\n : undefined;\n const reviewItem = mergeRequestKey\n ? items.find(\n item => item.externalSource?.integrationId === 'gitlab' && item.externalSource.externalId === mergeRequestKey,\n )\n : undefined;\n const headBranch = string(mergeRequest?.source_branch) ?? '';\n const authoringItem = headBranch\n ? items.find(\n item =>\n item.externalSource?.type !== 'pull-request' &&\n Object.values(item.sessions).some(session => session.branch === headBranch),\n )\n : undefined;\n const actorTrusted = await this.#trusted(connectionIds, String(projectId), username);\n const eventUserId = number(object(parsed.payload.user)?.id) ?? number(parsed.payload.user_id);\n const resolveAuthor = async (\n item: Record<string, unknown> | undefined,\n kind: 'issue' | 'merge_request',\n iid: number | undefined,\n ): Promise<string | undefined> => {\n if (!item || !iid) return undefined;\n const embedded = string(object(item.author)?.username);\n if (embedded) return embedded;\n if (eventUserId && eventUserId === number(item.author_id)) return username;\n for (const connectionId of connectionIds) {\n try {\n const resolved = await this.options.gitlab.getWorkItemAuthorUsername(\n connectionId,\n String(projectId),\n kind,\n iid,\n );\n if (resolved) return resolved;\n } catch {\n // Another active credential may cover this project; otherwise leave author trust unset.\n }\n }\n return undefined;\n };\n const [issueAuthor, mergeRequestAuthor] = await Promise.all([\n resolveAuthor(issue, 'issue', issueIid),\n resolveAuthor(mergeRequest, 'merge_request', mergeRequestIid),\n ]);\n const authorTrusted = async (author: string | undefined): Promise<boolean> => {\n if (!author) return false;\n return author === username ? actorTrusted : this.#trusted(connectionIds, String(projectId), author);\n };\n const [issueAuthorTrusted, mergeRequestAuthorTrusted] = await Promise.all([\n authorTrusted(issueAuthor),\n authorTrusted(mergeRequestAuthor),\n ]);\n const actor: FactoryRuleActor = {\n type: 'gitlab',\n username,\n trusted: actorTrusted,\n factoryAuthored: false,\n };\n\n // A push moves the head a recorded review verdict was given on. Recording\n // the new head lets the verdict badge show that it is outdated, while the\n // verdict itself stays so the next pass still runs as a re-review. It is\n // recorded before the push is evaluated: the dispatcher can apply the\n // re-review transition as soon as it commits, and the pass it starts names\n // the head it covers, which must be the pushed one.\n const lastCommit = object(mergeRequest?.last_commit);\n const headSha = string(lastCommit?.id)?.toLowerCase();\n // Without an update time the commit time still orders the push; dropping\n // the write would leave the pass naming the head the last verdict covered.\n const headAt = timestamp(mergeRequest?.updated_at) ?? timestamp(lastCommit?.timestamp);\n let pushedReviewItem = reviewItem;\n if (event === 'mergeRequestUpdated' && headSha && reviewItem) {\n const parent = items.find(item => item.id === reviewItem.parentWorkItemId);\n // The authoring Work item mirrors the verdict of the merge request it has\n // out; a push to an older one must not mark its successor's verdict.\n await recordPullRequestHead(this.options.storage, {\n orgId: reviewItem.orgId,\n id: reviewItem.id,\n headSha,\n headAt,\n });\n if (parent) {\n await recordPullRequestHead(this.options.storage, {\n orgId: parent.orgId,\n id: parent.id,\n headSha,\n headAt,\n ownedPullRequestNumber: mergeRequestIid,\n });\n }\n pushedReviewItem = (await this.options.storage.get({ orgId: reviewItem.orgId, id: reviewItem.id })) ?? reviewItem;\n }\n const candidates: Array<WorkItemRow | undefined> =\n event === 'mergeRequestMerged'\n ? [...new Map([reviewItem, authoringItem].filter(Boolean).map(item => [item!.id, item!])).values()]\n : event === 'mergeRequestCommented'\n ? [authoringItem ?? reviewItem]\n : event === 'mergeRequestOpened'\n ? [authoringItem, ...(authoringItem ? [authoringItem] : [])]\n : event.startsWith('mergeRequest')\n ? [pushedReviewItem ?? authoringItem]\n : [issueItem];\n const results = [];\n for (const [index, item] of candidates.entries()) {\n results.push(\n await this.#evaluate({\n parsed,\n event,\n projectId,\n projectPath,\n host,\n actor,\n orgId: target.target.orgId,\n factoryProjectId: target.target.factoryProjectId,\n factoryProject,\n board: { board: board.id, initialPhase: board.initialPhase },\n issue,\n issueAuthor,\n issueAuthorTrusted,\n issueIid,\n issueSourceKey,\n mergeRequest,\n mergeRequestAuthor,\n mergeRequestAuthorTrusted,\n mergeRequestIid,\n mergeRequestKey,\n factoryAuthored: Boolean(authoringItem),\n item,\n ingressIdentity: `gitlab:${projectId}:${parsed.deliveryId}${index ? `:${item?.id ?? index}` : ''}`,\n // A merge request opening concerns two cards: its own Review card,\n // which the integration handler files against the authoring item,\n // and that authoring item, which its board may react to. The second\n // evaluation runs only the board handler, under an id-suffixed\n // identity so it is not dropped as a replay of the first.\n ...(event === 'mergeRequestOpened' ? { mergeRequestOpenedScope: index ? ('board' as const) : ('intake' as const) } : {}),\n }),\n );\n }\n // The authoring Work item records which merge request it has out, so the\n // Work transition policy keeps an agent from closing the work while it is\n // open. Sessions carry no repository, so a branch shared by two Work cards\n // is ambiguous and records nothing.\n const branchOwners = headBranch\n ? items.filter(\n item =>\n item.externalSource?.type !== 'pull-request' &&\n Object.values(item.sessions).some(session => session.branch === headBranch),\n )\n : [];\n if (authoringItem && mergeRequestIid && branchOwners.length === 1) {\n await this.#trackOpenMergeRequest({\n orgId: authoringItem.orgId,\n id: authoringItem.id,\n event,\n mergeRequestIid,\n eventAt: timestamp(mergeRequest?.updated_at),\n opened: string(mergeRequest?.state) === 'opened' && (actorTrusted || mergeRequestAuthorTrusted),\n headSha: string(object(mergeRequest?.last_commit)?.id)?.toLowerCase(),\n });\n }\n if (results.some(result => result.status === 'committed')) return { status: 'committed' };\n if (results.some(result => result.status === 'replayed')) return { status: 'replayed' };\n return results[0] ?? { status: 'ignored' };\n }\n\n /**\n * Records or clears the merge request a Work card has out. Each merge\n * request's last applied event time is kept on the card, so an opening that\n * is older than a close already applied (a replay, or a close delivered\n * first) is ignored, while a genuine reopen is newer and records it again.\n */\n async #trackOpenMergeRequest(input: {\n orgId: string;\n id: string;\n event: string;\n mergeRequestIid: number;\n eventAt: number | undefined;\n opened: boolean;\n headSha: string | undefined;\n }): Promise<void> {\n const settling = input.event === 'mergeRequestMerged' || input.event === 'mergeRequestClosed';\n if (input.event !== 'mergeRequestOpened' && !settling) return;\n const key = String(input.mergeRequestIid);\n for (let attempt = 0; attempt < 3; attempt++) {\n const item = await this.options.storage.get({ orgId: input.orgId, id: input.id });\n if (!item) return;\n const events = object(item.metadata?.mergeRequestEvents) ?? {};\n const last = object(events[key]);\n const lastAt = number(last?.at);\n if (input.eventAt !== undefined && lastAt !== undefined && lastAt >= input.eventAt) return;\n // Without a timestamp, only an opening that no close has settled is trusted.\n if (input.eventAt === undefined && !settling && last?.open === false) return;\n if (!settling && !input.opened) return;\n const recorded = item.metadata?.openPullRequestNumber;\n const patch: Record<string, unknown> = {\n mergeRequestEvents: { ...events, [key]: { at: input.eventAt ?? lastAt ?? null, open: !settling } },\n };\n if (!settling) {\n // A head recorded for an earlier merge request must not mark this one's\n // verdict outdated. A replayed opening of the merge request already\n // recorded keeps the head its pushes left.\n if (recorded !== input.mergeRequestIid) {\n patch.pullRequestHeadSha = input.headSha ?? null;\n patch.pullRequestHeadAt = input.eventAt ?? null;\n }\n patch.openPullRequestNumber = input.mergeRequestIid;\n } else if (recorded === input.mergeRequestIid) {\n patch.openPullRequestNumber = null;\n if (input.event === 'mergeRequestMerged' && typeof item.metadata?.reviewVerdict === 'string') {\n patch.reviewVerdict = null;\n }\n }\n try {\n await this.options.storage.update({\n orgId: item.orgId,\n id: item.id,\n userId: 'factory-rule-dispatcher',\n expectedRevision: item.revision,\n patch: { metadata: patch },\n });\n return;\n } catch (error) {\n if (!(error instanceof WorkItemUpdateConflictError)) throw error;\n }\n }\n }\n\n async #trusted(connectionIds: string[], projectId: string, username: string): Promise<boolean> {\n for (const connectionId of connectionIds) {\n try {\n const level = await this.options.gitlab.getProjectMemberAccessLevel(connectionId, projectId, username);\n if (level !== undefined) return level >= GITLAB_TRUSTED_ACCESS_LEVEL;\n } catch {\n // Try another active credential covering the same canonical project; otherwise fail closed.\n }\n }\n return false;\n }\n\n async #evaluate(\n input: {\n parsed: ParsedGitLabWebhook;\n event: FactoryGitLabEventName;\n projectId: number;\n projectPath: string;\n host: string;\n actor: FactoryRuleActor;\n orgId: string;\n factoryProjectId: string;\n factoryProject: { createdAt: Date };\n board: { board: string; initialPhase: string };\n issue: Record<string, unknown> | undefined;\n issueAuthor: string | undefined;\n issueAuthorTrusted: boolean;\n issueIid: number | undefined;\n issueSourceKey: string | undefined;\n mergeRequest: Record<string, unknown> | undefined;\n mergeRequestAuthor: string | undefined;\n mergeRequestAuthorTrusted: boolean;\n mergeRequestIid: number | undefined;\n mergeRequestKey: string | undefined;\n factoryAuthored: boolean;\n item: WorkItemRow | undefined;\n ingressIdentity: string;\n mergeRequestOpenedScope?: 'intake' | 'board';\n },\n attempt = 1,\n ): Promise<{ status: IngressStatus }> {\n const note = object(input.parsed.payload.object_attributes);\n const issueAuthor = input.issueAuthor;\n const mergeRequestAuthor = input.mergeRequestAuthor;\n const mergeRequestState = string(input.mergeRequest?.state);\n const context: FactoryGitLabRuleContext = {\n tenant: { orgId: input.orgId, projectId: input.factoryProjectId },\n actor: input.actor,\n ingress: { type: 'gitlab', id: input.ingressIdentity },\n cause: `gitlab.${input.event}`,\n causalChain: [],\n configVersion: this.options.configVersion,\n ...(input.item\n ? {\n item: {\n id: input.item.id,\n source:\n input.item.externalSource?.type === 'pull-request'\n ? ('gitlab-pr' as const)\n : input.item.externalSource?.integrationId === 'gitlab'\n ? ('gitlab-issue' as const)\n : ('manual' as const),\n sourceKey: input.item.externalSource?.externalId ?? null,\n parentWorkItemId: input.item.parentWorkItemId,\n title: input.item.title,\n url: input.item.externalSource?.url ?? null,\n stages: input.item.stages,\n acceptedAt: input.item.acceptedAt,\n metadata: input.item.metadata,\n },\n board: boardForWorkItem(input.item),\n itemRevision: input.item.revision,\n ...withBoardClosure(this.options.boards, input.item),\n ...rulePhaseField(this.options.boards, input.item),\n }\n : {}),\n intake: input.board,\n event: input.event,\n deliveryId: input.parsed.deliveryId,\n factory: { createdAt: input.factoryProject.createdAt.toISOString() },\n repository: { id: input.projectId, fullName: input.projectPath, host: input.host },\n ...(input.issueIid && input.issueSourceKey && string(input.issue?.title)\n ? {\n issue: {\n number: input.issueIid,\n sourceKey: input.issueSourceKey,\n title: string(input.issue?.title)!,\n url:\n string(input.issue?.url) ??\n string(input.issue?.web_url) ??\n `https://${input.host}/${input.projectPath}/-/issues/${input.issueIid}`,\n ...(string(input.issue?.created_at) ? { createdAt: string(input.issue?.created_at) } : {}),\n ...(string(input.issue?.updated_at) ? { updatedAt: string(input.issue?.updated_at) } : {}),\n assignees: usernames(input.parsed.payload.assignees ?? input.issue?.assignees),\n labels: labelNames(input.issue?.labels ?? input.parsed.payload.labels),\n labelColors: labelColors(input.issue?.labels ?? input.parsed.payload.labels),\n state: string(input.issue?.state) === 'closed' ? ('closed' as const) : ('open' as const),\n ...(issueAuthor ? { author: issueAuthor } : {}),\n authorTrusted: input.issueAuthorTrusted,\n },\n }\n : {}),\n ...(input.parsed.event === 'Note Hook' && number(note?.id)\n ? {\n note: {\n id: number(note?.id)!,\n ...(string(note?.note) ? { body: string(note?.note) } : {}),\n ...(string(note?.url) ? { url: string(note?.url) } : {}),\n ...(string(object(input.parsed.payload.user)?.username)\n ? { author: string(object(input.parsed.payload.user)?.username) }\n : {}),\n ...(string(note?.created_at) ? { createdAt: string(note?.created_at) } : {}),\n ...(string(note?.updated_at) ? { updatedAt: string(note?.updated_at) } : {}),\n },\n }\n : {}),\n ...(input.mergeRequestIid && input.mergeRequestKey && string(input.mergeRequest?.title)\n ? {\n mergeRequest: {\n number: input.mergeRequestIid,\n sourceKey: input.mergeRequestKey,\n title: string(input.mergeRequest?.title)!,\n url:\n string(input.mergeRequest?.url) ??\n string(input.mergeRequest?.web_url) ??\n `https://${input.host}/${input.projectPath}/-/merge_requests/${input.mergeRequestIid}`,\n ...(string(input.mergeRequest?.created_at) ? { createdAt: string(input.mergeRequest?.created_at) } : {}),\n state:\n mergeRequestState === 'closed' || mergeRequestState === 'merged'\n ? ('closed' as const)\n : ('open' as const),\n draft: boolean(input.mergeRequest?.draft) ?? boolean(input.mergeRequest?.work_in_progress) ?? false,\n merged: input.event === 'mergeRequestMerged' || mergeRequestState === 'merged',\n assignees: usernames(input.parsed.payload.assignees ?? input.mergeRequest?.assignees),\n reviewers: usernames(input.parsed.payload.reviewers ?? input.mergeRequest?.reviewers),\n labels: labelNames(input.mergeRequest?.labels ?? input.parsed.payload.labels),\n labelColors: labelColors(input.mergeRequest?.labels ?? input.parsed.payload.labels),\n ...(mergeRequestAuthor ? { author: mergeRequestAuthor } : {}),\n authorTrusted: input.mergeRequestAuthorTrusted,\n factoryAuthored: input.factoryAuthored,\n headBranch: string(input.mergeRequest?.source_branch) ?? '',\n baseBranch: string(input.mergeRequest?.target_branch) ?? '',\n },\n }\n : {}),\n };\n const boardRules = boardEventHandlers(\n this.options.boards,\n input.mergeRequestOpenedScope === 'intake' ? undefined : eventBoardId(context),\n 'gitlab',\n );\n const rule =\n input.mergeRequestOpenedScope === 'board'\n ? hasExplicitRuleOverride(this.options.gitlab.rules, input.event)\n ? undefined\n : boardRules?.mergeRequestOpened\n : selectEventRule(this.options.gitlab.rules, boardRules, input.event);\n // Without a board handler the authoring card has nothing to answer; skip\n // recording an empty evaluation for it.\n if (input.mergeRequestOpenedScope === 'board' && !rule) return { status: 'ignored' };\n let decision: FactoryRuleDecision | void;\n let decisions: Record<string, unknown>[] = [];\n let outcome: { status: 'accepted' | 'rejected'; code?: string; reason?: string } = { status: 'accepted' };\n try {\n decision = rule ? await withRuleTimeout(Promise.resolve(rule(Object.freeze(context)))) : undefined;\n if (decision?.type === 'reject') {\n outcome = { status: 'rejected', code: decision.code, reason: decision.reason };\n } else if (decision) {\n decisions = validateFactoryRuleDecisions([decision]).map(entry => {\n assertFactoryDecisionTarget(\n entry,\n this.options.boards,\n input.item ? boardForWorkItem(input.item) : undefined,\n );\n return { ...entry };\n });\n }\n } catch (error) {\n const timedOut = error instanceof Error && error.message === 'FACTORY_RULE_TIMEOUT';\n outcome = {\n status: 'rejected',\n code: timedOut ? 'timeout' : 'rule_error',\n reason: timedOut\n ? 'Factory rule evaluation timed out.'\n : error instanceof Error\n ? error.message.slice(0, 2_000)\n : 'Factory GitLab rule failed.',\n };\n }\n\n const committed = await this.options.storage.commitRuleEvaluation({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n workItemId: input.item?.id ?? null,\n ingress: { identity: input.ingressIdentity, triggerType: `gitlab.${input.event}` },\n configVersion: this.options.configVersion,\n expectedRevision: input.item?.revision ?? null,\n actor: { ...input.actor },\n outcome,\n decisions,\n causalChain: [],\n now: new Date(),\n });\n if (committed.status === 'stale') {\n // Nothing was persisted: re-read the item and evaluate again at its fresh revision.\n if (attempt >= MAX_STALE_RULE_ATTEMPTS) {\n throw new Error(`Factory GitLab rule evaluation for ${input.ingressIdentity} kept losing revision races.`);\n }\n const fresh = input.item ? await this.options.storage.get({ orgId: input.orgId, id: input.item.id }) : null;\n if (input.item && !fresh) return { status: 'missing' };\n return this.#evaluate({ ...input, item: fresh ?? undefined }, attempt + 1);\n }\n return { status: committed.status };\n }\n}\n\nexport function attachGitLabRules(\n gitlab: GitLabRulesIntegration,\n context: IntegrationContext,\n): ((event: ParsedGitLabWebhook) => Promise<unknown>) | undefined {\n if (!context.runtime) return undefined;\n const rules = new GitLabRules({\n gitlab,\n sourceControl: context.storage.sourceControl,\n projects: context.storage.projects,\n storage: context.runtime.workItems,\n intake: context.storage.intake,\n configVersion: context.runtime.configVersion,\n boards: context.runtime.boards,\n });\n return event => rules.ingest(event);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rules.js","names":[],"sources":["../../../src/integrations/gitlab/rules.ts"],"sourcesContent":["import {\n boardEventHandlers,\n boardForWorkItem,\n eventBoardId,\n rulePhaseField,\n withBoardClosure,\n} from '../../boards/index.js';\nimport type { BoardRegistry } from '../../boards/index.js';\nimport { hasExplicitRuleOverride, selectEventRule } from '../../rules/event-rules.js';\nimport type {\n FactoryGitLabEventName,\n FactoryGitLabRuleContext,\n FactoryRuleActor,\n FactoryRuleDecision,\n} from '../../rules/types.js';\nimport { assertFactoryDecisionTarget, validateFactoryRuleDecisions } from '../../rules/validation.js';\nimport type { IntakeStorage } from '../../storage/domains/intake/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type {\n ExternalRepositoryProjectTarget,\n SourceControlStorageHandle,\n} from '../../storage/domains/source-control/base.js';\nimport { WorkItemUpdateConflictError } from '../../storage/domains/work-items/base.js';\nimport type { WorkItemRow, WorkItemsStorage } from '../../storage/domains/work-items/base.js';\nimport type { IntegrationContext } from '../base.js';\nimport { recordPullRequestHead } from '../pull-request-head.js';\nimport type { GitLabEventRules } from './default-rules.js';\nimport { encodeIssueReference, encodeSourceId, GITLAB_TRUSTED_ACCESS_LEVEL } from './integration.js';\nimport type { ParsedGitLabWebhook } from './webhook.js';\n\nconst RULE_TIMEOUT_MS = 5_000;\n\ntype IngressStatus = 'ignored' | 'committed' | 'replayed' | 'missing';\n\nfunction object(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;\n}\n\nfunction string(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction number(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\n/** GitLab sends `2024-01-02T03:04:05Z`, `2024-01-02 03:04:05 UTC` or `2024-01-02 03:04:05 +0000`. */\nfunction timestamp(value: unknown): number | undefined {\n const text = string(value);\n if (!text) return undefined;\n const spaced = /^(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?) (?:UTC|([+-]\\d{2}):?(\\d{2}))$/.exec(text);\n const parsed = Date.parse(\n spaced ? `${spaced[1]}T${spaced[2]}${spaced[3] ? `${spaced[3]}:${spaced[4]}` : 'Z'}` : text,\n );\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction boolean(value: unknown): boolean | undefined {\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction usernames(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.flatMap(entry => {\n const username = string(object(entry)?.username);\n return username ? [username] : [];\n });\n}\n\nfunction labelNames(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.flatMap(entry => {\n if (typeof entry === 'string') return entry ? [entry] : [];\n const title = string(object(entry)?.title) ?? string(object(entry)?.name);\n return title ? [title] : [];\n });\n}\n\nfunction labelColors(value: unknown): Record<string, string> {\n if (!Array.isArray(value)) return {};\n return Object.fromEntries(\n value.flatMap(entry => {\n const label = object(entry);\n const name = string(label?.title) ?? string(label?.name);\n const color = string(label?.color);\n return name && color ? [[name, color]] : [];\n }),\n );\n}\n\nfunction eventName(parsed: ParsedGitLabWebhook): FactoryGitLabEventName | undefined {\n const attributes = object(parsed.payload.object_attributes);\n const action = string(attributes?.action)?.toLowerCase();\n if (parsed.event === 'Issue Hook') {\n if (action === 'open' || action === 'reopen') return 'issueOpened';\n if (action === 'close') return 'issueClosed';\n return 'issueUpdated';\n }\n if (parsed.event === 'Note Hook') {\n const noteableType = string(attributes?.noteable_type);\n if (noteableType === 'Issue') return 'issueCommented';\n if (noteableType === 'MergeRequest') return 'mergeRequestCommented';\n return;\n }\n if (parsed.event !== 'Merge Request Hook') return;\n if (action === 'open' || action === 'reopen') return 'mergeRequestOpened';\n if (action === 'merge') return 'mergeRequestMerged';\n if (action === 'close') return 'mergeRequestClosed';\n return 'mergeRequestUpdated';\n}\n\nfunction normalizeHost(host: string): string {\n return host.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nfunction hostFromPayload(parsed: ParsedGitLabWebhook): string | undefined {\n const declaredHost = parsed.instanceHost ? normalizeHost(parsed.instanceHost) : undefined;\n const projectUrl = string(object(parsed.payload.project)?.web_url);\n if (!projectUrl) return declaredHost;\n try {\n const payloadHost = normalizeHost(new URL(projectUrl).host);\n if (declaredHost && declaredHost !== payloadHost) return undefined;\n return payloadHost;\n } catch {\n return undefined;\n }\n}\n\nfunction mergeRequestSourceKey(host: string, projectId: number, mergeRequestIid: number): string {\n return `gitlab-pr:${Buffer.from(JSON.stringify({ version: 1, host, projectId, mergeRequestIid }), 'utf8').toString('base64url')}`;\n}\n\nasync function withRuleTimeout<T>(promise: Promise<T>): Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(new Error('FACTORY_RULE_TIMEOUT')), RULE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timeout) clearTimeout(timeout);\n }\n}\n\nconst MAX_STALE_RULE_ATTEMPTS = 3;\n\nexport interface GitLabRulesIntegration {\n readonly rules: GitLabEventRules;\n resolveActiveConnectionForHost?(storedConnectionId: string, host: string): Promise<string>;\n getProjectMemberAccessLevel(connectionId: string, projectId: string, username: string): Promise<number | undefined>;\n getWorkItemAuthorUsername(\n connectionId: string,\n projectId: string,\n kind: 'issue' | 'merge_request',\n iid: number,\n ): Promise<string | undefined>;\n}\n\ninterface GitLabRulesOptions {\n gitlab: GitLabRulesIntegration;\n sourceControl: SourceControlStorageHandle;\n projects: FactoryProjectsStorage;\n storage: WorkItemsStorage;\n intake: IntakeStorage;\n configVersion: string;\n boards: BoardRegistry;\n}\n\ninterface ProjectTarget {\n target: ExternalRepositoryProjectTarget;\n connectionIds: string[];\n}\n\nexport class GitLabRules {\n constructor(private readonly options: GitLabRulesOptions) {}\n\n async ingest(parsed: ParsedGitLabWebhook): Promise<{ status: IngressStatus }> {\n const event = eventName(parsed);\n const project = object(parsed.payload.project);\n const projectId = number(project?.id);\n const projectPath = string(project?.path_with_namespace);\n const host = hostFromPayload(parsed);\n const username = string(parsed.payload.user_username) ?? string(object(parsed.payload.user)?.username);\n if (!event || !projectId || !projectPath || !host || !username) return { status: 'ignored' };\n\n const targets = await this.#targets(String(projectId), host);\n if (targets.length === 0) return { status: 'ignored' };\n const results: Array<{ status: IngressStatus }> = [];\n for (const target of targets) {\n results.push(await this.#ingestProject(parsed, event, projectId, projectPath, host, username, target));\n }\n if (results.some(result => result.status === 'committed')) return { status: 'committed' };\n if (results.some(result => result.status === 'replayed')) return { status: 'replayed' };\n return results[0] ?? { status: 'ignored' };\n }\n\n async #targets(repositoryExternalId: string, host: string): Promise<ProjectTarget[]> {\n const grouped = new Map<string, ProjectTarget>();\n const keys = (await this.options.sourceControl.projectRepositories.listConfiguredExternalKeys()).filter(\n key => key.repositoryExternalId === repositoryExternalId,\n );\n for (const key of keys) {\n const targets = await this.options.sourceControl.projectRepositories.listByExternalRepository({\n installationExternalId: key.installationExternalId,\n repositoryExternalId,\n });\n for (const target of targets) {\n const installation = await this.options.sourceControl.installations.findByExternalId({\n orgId: target.orgId,\n externalId: key.installationExternalId,\n });\n const installationHost = string(installation?.providerMetadata.host);\n if (!installationHost || normalizeHost(installationHost) !== normalizeHost(host)) continue;\n const id = `${target.orgId}:${target.factoryProjectId}`;\n const current = grouped.get(id);\n if (current) {\n if (!current.connectionIds.includes(key.installationExternalId)) {\n current.connectionIds.push(key.installationExternalId);\n }\n } else {\n grouped.set(id, { target, connectionIds: [key.installationExternalId] });\n }\n }\n }\n return [...grouped.values()];\n }\n\n async #ingestProject(\n parsed: ParsedGitLabWebhook,\n event: FactoryGitLabEventName,\n projectId: number,\n projectPath: string,\n host: string,\n username: string,\n target: ProjectTarget,\n ): Promise<{ status: IngressStatus }> {\n const factoryProject = await this.options.projects.get({\n orgId: target.target.orgId,\n id: target.target.factoryProjectId,\n });\n if (!factoryProject) return { status: 'missing' };\n\n const sourceId = encodeSourceId({ host, projectId: String(projectId) });\n const config = await this.options.intake.getConfig({\n orgId: target.target.orgId,\n integrationIds: ['gitlab'],\n });\n if (!config.gitlab?.enabled || !config.gitlab.sourceIds?.includes(sourceId)) return { status: 'ignored' };\n const binding = (\n await this.options.intake.listBindings({ orgId: target.target.orgId, integrationId: 'gitlab' })\n ).find(\n candidate => candidate.sourceId === sourceId && candidate.factoryProjectId === target.target.factoryProjectId,\n );\n if (!binding) return { status: 'ignored' };\n const board = this.options.boards.get(binding.board ?? 'work');\n if (!board) return { status: 'ignored' };\n\n const connectionIds = [\n ...new Set(\n await Promise.all(\n target.connectionIds.map(\n connectionId => this.options.gitlab.resolveActiveConnectionForHost?.(connectionId, host) ?? connectionId,\n ),\n ),\n ),\n ];\n\n const attributes = object(parsed.payload.object_attributes);\n const issue = parsed.event === 'Note Hook' ? object(parsed.payload.issue) : attributes;\n const mergeRequest = parsed.event === 'Note Hook' ? object(parsed.payload.merge_request) : attributes;\n const issueIid = number(issue?.iid);\n const mergeRequestIid = number(mergeRequest?.iid);\n const issueSourceKey = issueIid\n ? encodeIssueReference({ host, projectId: String(projectId), issueIid })\n : undefined;\n const mergeRequestKey = mergeRequestIid ? mergeRequestSourceKey(host, projectId, mergeRequestIid) : undefined;\n const items = await this.options.storage.list({\n orgId: target.target.orgId,\n factoryProjectId: target.target.factoryProjectId,\n });\n const issueItem = issueSourceKey\n ? items.find(\n item => item.externalSource?.integrationId === 'gitlab' && item.externalSource.externalId === issueSourceKey,\n )\n : undefined;\n const reviewItem = mergeRequestKey\n ? items.find(\n item => item.externalSource?.integrationId === 'gitlab' && item.externalSource.externalId === mergeRequestKey,\n )\n : undefined;\n const headBranch = string(mergeRequest?.source_branch) ?? '';\n const authoringItem = headBranch\n ? items.find(\n item =>\n item.externalSource?.type !== 'pull-request' &&\n Object.values(item.sessions).some(session => session.branch === headBranch),\n )\n : undefined;\n const actorTrusted = await this.#trusted(connectionIds, String(projectId), username);\n const eventUserId = number(object(parsed.payload.user)?.id) ?? number(parsed.payload.user_id);\n const resolveAuthor = async (\n item: Record<string, unknown> | undefined,\n kind: 'issue' | 'merge_request',\n iid: number | undefined,\n ): Promise<string | undefined> => {\n if (!item || !iid) return undefined;\n const embedded = string(object(item.author)?.username);\n if (embedded) return embedded;\n if (eventUserId && eventUserId === number(item.author_id)) return username;\n for (const connectionId of connectionIds) {\n try {\n const resolved = await this.options.gitlab.getWorkItemAuthorUsername(\n connectionId,\n String(projectId),\n kind,\n iid,\n );\n if (resolved) return resolved;\n } catch {\n // Another active credential may cover this project; otherwise leave author trust unset.\n }\n }\n return undefined;\n };\n const [issueAuthor, mergeRequestAuthor] = await Promise.all([\n resolveAuthor(issue, 'issue', issueIid),\n resolveAuthor(mergeRequest, 'merge_request', mergeRequestIid),\n ]);\n const authorTrusted = async (author: string | undefined): Promise<boolean> => {\n if (!author) return false;\n return author === username ? actorTrusted : this.#trusted(connectionIds, String(projectId), author);\n };\n const [issueAuthorTrusted, mergeRequestAuthorTrusted] = await Promise.all([\n authorTrusted(issueAuthor),\n authorTrusted(mergeRequestAuthor),\n ]);\n const actor: FactoryRuleActor = {\n type: 'gitlab',\n username,\n trusted: actorTrusted,\n factoryAuthored: false,\n };\n\n // A push moves the head a recorded review verdict was given on. Recording\n // the new head lets the verdict badge show that it is outdated, while the\n // verdict itself stays so the next pass still runs as a re-review. It is\n // recorded before the push is evaluated: the dispatcher can apply the\n // re-review transition as soon as it commits, and the pass it starts names\n // the head it covers, which must be the pushed one.\n const lastCommit = object(mergeRequest?.last_commit);\n const headSha = string(lastCommit?.id)?.toLowerCase();\n // Without an update time the commit time still orders the push; dropping\n // the write would leave the pass naming the head the last verdict covered.\n const headAt = timestamp(mergeRequest?.updated_at) ?? timestamp(lastCommit?.timestamp);\n let pushedReviewItem = reviewItem;\n if (event === 'mergeRequestUpdated' && headSha && reviewItem) {\n const parent = items.find(item => item.id === reviewItem.parentWorkItemId);\n // The authoring Work item mirrors the verdict of the merge request it has\n // out; a push to an older one must not mark its successor's verdict.\n await recordPullRequestHead(this.options.storage, {\n orgId: reviewItem.orgId,\n id: reviewItem.id,\n headSha,\n headAt,\n });\n if (parent) {\n await recordPullRequestHead(this.options.storage, {\n orgId: parent.orgId,\n id: parent.id,\n headSha,\n headAt,\n ownedPullRequestNumber: mergeRequestIid,\n });\n }\n pushedReviewItem = (await this.options.storage.get({ orgId: reviewItem.orgId, id: reviewItem.id })) ?? reviewItem;\n }\n const candidates: Array<WorkItemRow | undefined> =\n event === 'mergeRequestMerged'\n ? [...new Map([reviewItem, authoringItem].filter(Boolean).map(item => [item!.id, item!])).values()]\n : event === 'mergeRequestCommented'\n ? [authoringItem ?? reviewItem]\n : event === 'mergeRequestOpened'\n ? [authoringItem, ...(authoringItem ? [authoringItem] : [])]\n : event.startsWith('mergeRequest')\n ? [pushedReviewItem ?? authoringItem]\n : [issueItem];\n const results = [];\n for (const [index, item] of candidates.entries()) {\n results.push(\n await this.#evaluate({\n parsed,\n event,\n projectId,\n projectPath,\n host,\n actor,\n orgId: target.target.orgId,\n factoryProjectId: target.target.factoryProjectId,\n factoryProject,\n board: { board: board.id, initialPhase: board.initialPhase },\n issue,\n issueAuthor,\n issueAuthorTrusted,\n issueIid,\n issueSourceKey,\n mergeRequest,\n mergeRequestAuthor,\n mergeRequestAuthorTrusted,\n mergeRequestIid,\n mergeRequestKey,\n factoryAuthored: Boolean(authoringItem),\n item,\n ingressIdentity: `gitlab:${projectId}:${parsed.deliveryId}${index ? `:${item?.id ?? index}` : ''}`,\n // A merge request opening concerns two cards: its own Review card,\n // which the integration handler files against the authoring item,\n // and that authoring item, which its board may react to. The second\n // evaluation runs only the board handler, under an id-suffixed\n // identity so it is not dropped as a replay of the first.\n ...(event === 'mergeRequestOpened' ? { mergeRequestOpenedScope: index ? ('board' as const) : ('intake' as const) } : {}),\n }),\n );\n }\n // The authoring Work item records which merge request it has out, so the\n // Work transition policy keeps an agent from closing the work while it is\n // open. Sessions carry no repository, so a branch shared by two Work cards\n // is ambiguous and records nothing.\n const branchOwners = headBranch\n ? items.filter(\n item =>\n item.externalSource?.type !== 'pull-request' &&\n Object.values(item.sessions).some(session => session.branch === headBranch),\n )\n : [];\n if (authoringItem && mergeRequestIid && branchOwners.length === 1) {\n await this.#trackOpenMergeRequest({\n orgId: authoringItem.orgId,\n id: authoringItem.id,\n event,\n mergeRequestIid,\n eventAt: timestamp(mergeRequest?.updated_at),\n opened: string(mergeRequest?.state) === 'opened' && (actorTrusted || mergeRequestAuthorTrusted),\n headSha: string(object(mergeRequest?.last_commit)?.id)?.toLowerCase(),\n });\n }\n if (results.some(result => result.status === 'committed')) return { status: 'committed' };\n if (results.some(result => result.status === 'replayed')) return { status: 'replayed' };\n return results[0] ?? { status: 'ignored' };\n }\n\n /**\n * Records or clears the merge request a Work card has out. Each merge\n * request's last applied event time is kept on the card, so an opening that\n * is older than a close already applied (a replay, or a close delivered\n * first) is ignored, while a genuine reopen is newer and records it again.\n */\n async #trackOpenMergeRequest(input: {\n orgId: string;\n id: string;\n event: string;\n mergeRequestIid: number;\n eventAt: number | undefined;\n opened: boolean;\n headSha: string | undefined;\n }): Promise<void> {\n const settling = input.event === 'mergeRequestMerged' || input.event === 'mergeRequestClosed';\n if (input.event !== 'mergeRequestOpened' && !settling) return;\n const key = String(input.mergeRequestIid);\n for (let attempt = 0; attempt < 3; attempt++) {\n const item = await this.options.storage.get({ orgId: input.orgId, id: input.id });\n if (!item) return;\n const events = object(item.metadata?.mergeRequestEvents) ?? {};\n const last = object(events[key]);\n const lastAt = number(last?.at);\n if (input.eventAt !== undefined && lastAt !== undefined && lastAt >= input.eventAt) return;\n // Without a timestamp, only an opening that no close has settled is trusted.\n if (input.eventAt === undefined && !settling && last?.open === false) return;\n if (!settling && !input.opened) return;\n const recorded = item.metadata?.openPullRequestNumber;\n const patch: Record<string, unknown> = {\n mergeRequestEvents: { ...events, [key]: { at: input.eventAt ?? lastAt ?? null, open: !settling } },\n };\n if (!settling) {\n // A head recorded for an earlier merge request must not mark this one's\n // verdict outdated. A replayed opening of the merge request already\n // recorded keeps the head its pushes left.\n if (recorded !== input.mergeRequestIid) {\n patch.pullRequestHeadSha = input.headSha ?? null;\n patch.pullRequestHeadAt = input.eventAt ?? null;\n }\n patch.openPullRequestNumber = input.mergeRequestIid;\n } else if (recorded === input.mergeRequestIid) {\n patch.openPullRequestNumber = null;\n if (input.event === 'mergeRequestMerged' && typeof item.metadata?.reviewVerdict === 'string') {\n patch.reviewVerdict = null;\n }\n }\n try {\n await this.options.storage.update({\n orgId: item.orgId,\n id: item.id,\n userId: 'factory-rule-dispatcher',\n expectedRevision: item.revision,\n patch: { metadata: patch },\n });\n return;\n } catch (error) {\n if (!(error instanceof WorkItemUpdateConflictError)) throw error;\n }\n }\n }\n\n async #trusted(connectionIds: string[], projectId: string, username: string): Promise<boolean> {\n for (const connectionId of connectionIds) {\n try {\n const level = await this.options.gitlab.getProjectMemberAccessLevel(connectionId, projectId, username);\n if (level !== undefined) return level >= GITLAB_TRUSTED_ACCESS_LEVEL;\n } catch {\n // Try another active credential covering the same canonical project; otherwise fail closed.\n }\n }\n return false;\n }\n\n async #evaluate(\n input: {\n parsed: ParsedGitLabWebhook;\n event: FactoryGitLabEventName;\n projectId: number;\n projectPath: string;\n host: string;\n actor: FactoryRuleActor;\n orgId: string;\n factoryProjectId: string;\n factoryProject: { createdAt: Date };\n board: { board: string; initialPhase: string };\n issue: Record<string, unknown> | undefined;\n issueAuthor: string | undefined;\n issueAuthorTrusted: boolean;\n issueIid: number | undefined;\n issueSourceKey: string | undefined;\n mergeRequest: Record<string, unknown> | undefined;\n mergeRequestAuthor: string | undefined;\n mergeRequestAuthorTrusted: boolean;\n mergeRequestIid: number | undefined;\n mergeRequestKey: string | undefined;\n factoryAuthored: boolean;\n item: WorkItemRow | undefined;\n ingressIdentity: string;\n mergeRequestOpenedScope?: 'intake' | 'board';\n },\n attempt = 1,\n ): Promise<{ status: IngressStatus }> {\n const note = object(input.parsed.payload.object_attributes);\n const issueAuthor = input.issueAuthor;\n const mergeRequestAuthor = input.mergeRequestAuthor;\n const mergeRequestState = string(input.mergeRequest?.state);\n const context: FactoryGitLabRuleContext = {\n tenant: { orgId: input.orgId, projectId: input.factoryProjectId },\n actor: input.actor,\n ingress: { type: 'gitlab', id: input.ingressIdentity },\n cause: `gitlab.${input.event}`,\n causalChain: [],\n configVersion: this.options.configVersion,\n ...(input.item\n ? {\n item: {\n id: input.item.id,\n source:\n input.item.externalSource?.type === 'pull-request'\n ? ('gitlab-pr' as const)\n : input.item.externalSource?.integrationId === 'gitlab'\n ? ('gitlab-issue' as const)\n : ('manual' as const),\n sourceKey: input.item.externalSource?.externalId ?? null,\n parentWorkItemId: input.item.parentWorkItemId,\n title: input.item.title,\n url: input.item.externalSource?.url ?? null,\n stages: input.item.stages,\n acceptedAt: input.item.acceptedAt,\n metadata: input.item.metadata,\n },\n board: boardForWorkItem(input.item),\n itemRevision: input.item.revision,\n ...withBoardClosure(this.options.boards, input.item),\n ...rulePhaseField(this.options.boards, input.item),\n }\n : {}),\n intake: input.board,\n event: input.event,\n deliveryId: input.parsed.deliveryId,\n factory: { createdAt: input.factoryProject.createdAt.toISOString() },\n repository: { id: input.projectId, fullName: input.projectPath, host: input.host },\n ...(input.issueIid && input.issueSourceKey && string(input.issue?.title)\n ? {\n issue: {\n number: input.issueIid,\n sourceKey: input.issueSourceKey,\n title: string(input.issue?.title)!,\n url:\n string(input.issue?.url) ??\n string(input.issue?.web_url) ??\n `https://${input.host}/${input.projectPath}/-/issues/${input.issueIid}`,\n ...(string(input.issue?.created_at) ? { createdAt: string(input.issue?.created_at) } : {}),\n ...(string(input.issue?.updated_at) ? { updatedAt: string(input.issue?.updated_at) } : {}),\n assignees: usernames(input.parsed.payload.assignees ?? input.issue?.assignees),\n labels: labelNames(input.issue?.labels ?? input.parsed.payload.labels),\n labelColors: labelColors(input.issue?.labels ?? input.parsed.payload.labels),\n state: string(input.issue?.state) === 'closed' ? ('closed' as const) : ('open' as const),\n ...(issueAuthor ? { author: issueAuthor } : {}),\n authorTrusted: input.issueAuthorTrusted,\n },\n }\n : {}),\n ...(input.parsed.event === 'Note Hook' && number(note?.id)\n ? {\n note: {\n id: number(note?.id)!,\n ...(string(note?.note) ? { body: string(note?.note) } : {}),\n ...(string(note?.url) ? { url: string(note?.url) } : {}),\n ...(string(object(input.parsed.payload.user)?.username)\n ? { author: string(object(input.parsed.payload.user)?.username) }\n : {}),\n ...(string(note?.created_at) ? { createdAt: string(note?.created_at) } : {}),\n ...(string(note?.updated_at) ? { updatedAt: string(note?.updated_at) } : {}),\n },\n }\n : {}),\n ...(input.mergeRequestIid && input.mergeRequestKey && string(input.mergeRequest?.title)\n ? {\n mergeRequest: {\n number: input.mergeRequestIid,\n sourceKey: input.mergeRequestKey,\n title: string(input.mergeRequest?.title)!,\n url:\n string(input.mergeRequest?.url) ??\n string(input.mergeRequest?.web_url) ??\n `https://${input.host}/${input.projectPath}/-/merge_requests/${input.mergeRequestIid}`,\n ...(string(input.mergeRequest?.created_at) ? { createdAt: string(input.mergeRequest?.created_at) } : {}),\n state:\n mergeRequestState === 'closed' || mergeRequestState === 'merged'\n ? ('closed' as const)\n : ('open' as const),\n draft: boolean(input.mergeRequest?.draft) ?? boolean(input.mergeRequest?.work_in_progress) ?? false,\n merged: input.event === 'mergeRequestMerged' || mergeRequestState === 'merged',\n assignees: usernames(input.parsed.payload.assignees ?? input.mergeRequest?.assignees),\n reviewers: usernames(input.parsed.payload.reviewers ?? input.mergeRequest?.reviewers),\n labels: labelNames(input.mergeRequest?.labels ?? input.parsed.payload.labels),\n labelColors: labelColors(input.mergeRequest?.labels ?? input.parsed.payload.labels),\n ...(mergeRequestAuthor ? { author: mergeRequestAuthor } : {}),\n authorTrusted: input.mergeRequestAuthorTrusted,\n factoryAuthored: input.factoryAuthored,\n headBranch: string(input.mergeRequest?.source_branch) ?? '',\n baseBranch: string(input.mergeRequest?.target_branch) ?? '',\n },\n }\n : {}),\n };\n const boardRules = boardEventHandlers(\n this.options.boards,\n input.mergeRequestOpenedScope === 'intake' ? undefined : eventBoardId(context),\n 'gitlab',\n );\n const rule =\n input.mergeRequestOpenedScope === 'board'\n ? hasExplicitRuleOverride(this.options.gitlab.rules, input.event)\n ? undefined\n : boardRules?.mergeRequestOpened\n : selectEventRule(this.options.gitlab.rules, boardRules, input.event);\n // Without a board handler the authoring card has nothing to answer; skip\n // recording an empty evaluation for it.\n if (input.mergeRequestOpenedScope === 'board' && !rule) return { status: 'ignored' };\n let decision: FactoryRuleDecision | void;\n let decisions: Record<string, unknown>[] = [];\n let outcome: { status: 'accepted' | 'rejected'; code?: string; reason?: string } = { status: 'accepted' };\n try {\n decision = rule ? await withRuleTimeout(Promise.resolve(rule(Object.freeze(context)))) : undefined;\n if (decision?.type === 'reject') {\n outcome = { status: 'rejected', code: decision.code, reason: decision.reason };\n } else if (decision) {\n decisions = validateFactoryRuleDecisions([decision]).map(entry => {\n assertFactoryDecisionTarget(\n entry,\n this.options.boards,\n input.item ? boardForWorkItem(input.item) : undefined,\n );\n return { ...entry };\n });\n }\n } catch (error) {\n const timedOut = error instanceof Error && error.message === 'FACTORY_RULE_TIMEOUT';\n outcome = {\n status: 'rejected',\n code: timedOut ? 'timeout' : 'rule_error',\n reason: timedOut\n ? 'Factory rule evaluation timed out.'\n : error instanceof Error\n ? error.message.slice(0, 2_000)\n : 'Factory GitLab rule failed.',\n };\n }\n\n const committed = await this.options.storage.commitRuleEvaluation({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n workItemId: input.item?.id ?? null,\n ingress: { identity: input.ingressIdentity, triggerType: `gitlab.${input.event}` },\n configVersion: this.options.configVersion,\n expectedRevision: input.item?.revision ?? null,\n actor: { ...input.actor },\n outcome,\n decisions,\n causalChain: [],\n now: new Date(),\n });\n if (committed.status === 'stale') {\n // Nothing was persisted: re-read the item and evaluate again at its fresh revision.\n if (attempt >= MAX_STALE_RULE_ATTEMPTS) {\n throw new Error(`Factory GitLab rule evaluation for ${input.ingressIdentity} kept losing revision races.`);\n }\n const fresh = input.item ? await this.options.storage.get({ orgId: input.orgId, id: input.item.id }) : null;\n if (input.item && !fresh) return { status: 'missing' };\n return this.#evaluate({ ...input, item: fresh ?? undefined }, attempt + 1);\n }\n return { status: committed.status };\n }\n}\n\nexport function attachGitLabRules(\n gitlab: GitLabRulesIntegration,\n context: IntegrationContext,\n): ((event: ParsedGitLabWebhook) => Promise<unknown>) | undefined {\n if (!context.runtime) return undefined;\n const rules = new GitLabRules({\n gitlab,\n sourceControl: context.storage.sourceControl,\n projects: context.storage.projects,\n storage: context.runtime.workItems,\n intake: context.storage.intake,\n configVersion: context.runtime.configVersion,\n boards: context.runtime.boards,\n });\n return event => rules.ingest(event);\n}\n"],"mappings":"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"}