@mastra/factory 0.25.0-alpha.5 → 0.25.0-alpha.7

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 (105) hide show
  1. package/README.md +3 -1
  2. package/dist/boards/work-transition-policy.d.ts.map +1 -1
  3. package/dist/boards/work-transition-policy.js +3 -3
  4. package/dist/boards/work-transition-policy.js.map +1 -1
  5. package/dist/default-thinking-level.d.ts +3 -0
  6. package/dist/default-thinking-level.d.ts.map +1 -0
  7. package/dist/default-thinking-level.js +5 -0
  8. package/dist/default-thinking-level.js.map +1 -0
  9. package/dist/factory.d.ts.map +1 -1
  10. package/dist/factory.js +19 -5
  11. package/dist/factory.js.map +1 -1
  12. package/dist/integrations/gitlab/integration.d.ts +4 -0
  13. package/dist/integrations/gitlab/integration.d.ts.map +1 -1
  14. package/dist/integrations/gitlab/integration.js +10 -1
  15. package/dist/integrations/gitlab/integration.js.map +1 -1
  16. package/dist/integrations/gitlab/routes.d.ts +6 -0
  17. package/dist/integrations/gitlab/routes.d.ts.map +1 -1
  18. package/dist/integrations/gitlab/routes.js +153 -1
  19. package/dist/integrations/gitlab/routes.js.map +1 -1
  20. package/dist/integrations/gitlab/rules.d.ts.map +1 -1
  21. package/dist/integrations/gitlab/rules.js +4 -1
  22. package/dist/integrations/gitlab/rules.js.map +1 -1
  23. package/dist/integrations/jira/api.d.ts +1 -1
  24. package/dist/integrations/jira/api.d.ts.map +1 -1
  25. package/dist/integrations/jira/api.js +1 -1
  26. package/dist/integrations/jira/api.js.map +1 -1
  27. package/dist/integrations/jira/integration.d.ts +3 -1
  28. package/dist/integrations/jira/integration.d.ts.map +1 -1
  29. package/dist/integrations/jira/integration.js +17 -2
  30. package/dist/integrations/jira/integration.js.map +1 -1
  31. package/dist/integrations/pull-request-head.d.ts +1 -0
  32. package/dist/integrations/pull-request-head.d.ts.map +1 -1
  33. package/dist/integrations/pull-request-head.js +2 -1
  34. package/dist/integrations/pull-request-head.js.map +1 -1
  35. package/dist/integrations/slack/channel-reply-processor.d.ts +1 -0
  36. package/dist/integrations/slack/channel-reply-processor.d.ts.map +1 -1
  37. package/dist/integrations/slack/channel-reply-processor.js +11 -3
  38. package/dist/integrations/slack/channel-reply-processor.js.map +1 -1
  39. package/dist/integrations/slack/channel-session-link-processor.d.ts +1 -0
  40. package/dist/integrations/slack/channel-session-link-processor.d.ts.map +1 -1
  41. package/dist/integrations/slack/channel-session-link-processor.js +1 -1
  42. package/dist/integrations/slack/channel-session-link-processor.js.map +1 -1
  43. package/dist/integrations/slack/reply-tool.d.ts.map +1 -1
  44. package/dist/integrations/slack/reply-tool.js +3 -11
  45. package/dist/integrations/slack/reply-tool.js.map +1 -1
  46. package/dist/integrations/slack/run-error-card.d.ts +3 -0
  47. package/dist/integrations/slack/run-error-card.d.ts.map +1 -0
  48. package/dist/integrations/slack/run-error-card.js +44 -0
  49. package/dist/integrations/slack/run-error-card.js.map +1 -0
  50. package/dist/integrations/slack/slack.d.ts.map +1 -1
  51. package/dist/integrations/slack/slack.js +5 -1
  52. package/dist/integrations/slack/slack.js.map +1 -1
  53. package/dist/links.d.ts +24 -0
  54. package/dist/links.d.ts.map +1 -0
  55. package/dist/links.js +40 -0
  56. package/dist/links.js.map +1 -0
  57. package/dist/routes/config.d.ts +3 -0
  58. package/dist/routes/config.d.ts.map +1 -1
  59. package/dist/routes/config.js +22 -1
  60. package/dist/routes/config.js.map +1 -1
  61. package/dist/routes/intake.js +5 -2
  62. package/dist/routes/intake.js.map +1 -1
  63. package/dist/routes/surface.js +1 -1
  64. package/dist/routes/surface.js.map +1 -1
  65. package/dist/rules/dispatcher.d.ts.map +1 -1
  66. package/dist/rules/dispatcher.js +8 -1
  67. package/dist/rules/dispatcher.js.map +1 -1
  68. package/dist/run-error.d.ts +13 -0
  69. package/dist/run-error.d.ts.map +1 -0
  70. package/dist/run-error.js +81 -0
  71. package/dist/run-error.js.map +1 -0
  72. package/dist/session/thinking-default.d.ts +1 -1
  73. package/dist/session/thinking-default.d.ts.map +1 -1
  74. package/dist/session/thinking-default.js +10 -11
  75. package/dist/session/thinking-default.js.map +1 -1
  76. package/dist/storage/domains/intake/base.d.ts +5 -0
  77. package/dist/storage/domains/intake/base.d.ts.map +1 -1
  78. package/dist/storage/domains/intake/base.js +9 -0
  79. package/dist/storage/domains/intake/base.js.map +1 -1
  80. package/dist/storage/domains/projects/base.d.ts +0 -1
  81. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  82. package/dist/storage/domains/projects/base.js +4 -4
  83. package/dist/storage/domains/projects/base.js.map +1 -1
  84. package/dist/tools/read-tools.d.ts.map +1 -1
  85. package/dist/tools/read-tools.js +18 -2
  86. package/dist/tools/read-tools.js.map +1 -1
  87. package/dist/tools/run-watch.d.ts +0 -2
  88. package/dist/tools/run-watch.d.ts.map +1 -1
  89. package/dist/tools/run-watch.js +3 -42
  90. package/dist/tools/run-watch.js.map +1 -1
  91. package/dist/tools/scope.js +1 -1
  92. package/dist/tools/scope.js.map +1 -1
  93. package/dist/tools/session-tools.d.ts.map +1 -1
  94. package/dist/tools/session-tools.js +21 -5
  95. package/dist/tools/session-tools.js.map +1 -1
  96. package/dist/tools/work-item-tools.d.ts.map +1 -1
  97. package/dist/tools/work-item-tools.js +7 -1
  98. package/dist/tools/work-item-tools.js.map +1 -1
  99. package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
  100. package/factory-skills/factory-plan/SKILL.md +1 -1
  101. package/factory-skills/factory-rereview/SKILL.md +2 -1
  102. package/factory-skills/factory-review/SKILL.md +2 -1
  103. package/factory-skills/factory-self-review/SKILL.md +1 -1
  104. package/factory-skills/factory-triage/SKILL.md +46 -23
  105. package/package.json +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","names":[],"sources":["../../../src/integrations/gitlab/routes.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { FactorySandbox } from '@mastra/core/workspace';\nimport type { Context } from 'hono';\nimport type { RouteAuth } from '../../routes/route.js';\nimport { sanitizeSegment } from '../../sandbox/workdir.js';\nimport type { AuditEmitter } from '../../storage/domains/audit/domain.js';\nimport type { IntakeStorage } from '../../storage/domains/intake/base.js';\nimport { GitLabApiError } from './api.js';\nimport {\n decodeIssueReference,\n decodeSourceId,\n encodeIssueReference,\n encodeSourceId,\n gitlabConnection,\n} from './integration.js';\nimport type { GitLabIntegrationBase } from './integration.js';\nimport { listMergeRequestSubscriptionsForThread, mergeRequestUrl } from './subscriptions.js';\nimport { handleGitLabWebhook } from './webhook.js';\nimport type { GitLabWebhookHandlerOptions, ParsedGitLabWebhook } from './webhook.js';\n\ntype RouteContext = Context;\n\nfunction loose(c: unknown): RouteContext {\n return c as RouteContext;\n}\n\nexport interface BuildGitLabRoutesOptions {\n gitlab?: GitLabIntegrationBase;\n auth?: RouteAuth;\n intake?: IntakeStorage;\n sandbox?: FactorySandbox;\n emitAudit?: AuditEmitter['emit'];\n webhookSecret?: string;\n ingestFactoryEvent?: (event: ParsedGitLabWebhook) => Promise<unknown>;\n /** Mounted agent controller; enables webhook delivery to subscribed sessions. */\n controller?: GitLabWebhookHandlerOptions['controller'];\n}\n\nasync function resolveOrgTenant(\n c: RouteContext,\n auth: RouteAuth,\n): Promise<{ tenant: { orgId: string; userId: string } } | { response: Response }> {\n await auth.ensureUser(c);\n const tenant = auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized', reason: 'auth_required' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n {\n error: 'organization_required',\n message: 'GitLab intake requires an organization. Personal accounts cannot use GitLab intake.',\n },\n 403,\n ),\n };\n }\n return { tenant: { orgId: tenant.orgId, userId: tenant.userId } };\n}\n\nfunction gitlabFetchError(c: RouteContext, error: unknown) {\n if (error instanceof GitLabApiError && error.code === 'gitlab_auth_failed') {\n return c.json({ error: 'gitlab_auth_failed', message: error.message }, 409);\n }\n return c.json({ error: 'gitlab_fetch_failed', message: error instanceof Error ? error.message : String(error) }, 502);\n}\n\ntype GitLabSourceIdentityMigration = { from: string; to: string };\n\nfunction gitlabProjectPayload(source: { id: string; name: string; metadata?: Record<string, unknown> }) {\n const connectionId = typeof source.metadata?.connectionId === 'string' ? source.metadata.connectionId : null;\n const projectId = typeof source.metadata?.projectId === 'string' ? source.metadata.projectId : null;\n const projectPath = typeof source.metadata?.projectPath === 'string' ? source.metadata.projectPath : source.name;\n if (!connectionId || !projectId) return null;\n const repositoryName = projectPath.split('/').filter(Boolean).at(-1) ?? 'repo';\n return {\n id: source.id,\n name: source.name,\n projectId,\n projectPath,\n connectionId,\n accountLabel: typeof source.metadata?.accountLabel === 'string' ? source.metadata.accountLabel : null,\n defaultBranch: typeof source.metadata?.defaultBranch === 'string' ? source.metadata.defaultBranch : 'main',\n sandboxProvider: 'none',\n sandboxWorkdir: `~/${sanitizeSegment(repositoryName)}`,\n };\n}\n\nfunction positiveId(value: string | undefined): number | null {\n const number = Number(value);\n return value && /^[1-9]\\d*$/.test(value) && Number.isSafeInteger(number) ? number : null;\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 reconcileGitLabSourceIdentity({\n intake,\n orgId,\n sources,\n}: {\n intake: IntakeStorage;\n orgId: string;\n sources: Array<{ id: string; metadata?: Record<string, unknown> }>;\n}): Promise<{ migrations: GitLabSourceIdentityMigration[]; conflicts: GitLabSourceIdentityMigration[] }> {\n await intake.ensureReady();\n const [config, bindings] = await Promise.all([\n intake.getConfig({ orgId }),\n intake.listBindings({ orgId, integrationId: 'gitlab' }),\n ]);\n const selected = config.gitlab?.sourceIds ?? [];\n const canonicalByProject = new Map<string, string[]>();\n for (const source of sources) {\n const reference = decodeSourceId(source.id);\n if (!reference?.host) continue;\n const ids = canonicalByProject.get(reference.projectId) ?? [];\n ids.push(source.id);\n canonicalByProject.set(reference.projectId, ids);\n }\n\n const migrations = new Map<string, string>();\n for (const sourceId of new Set([...selected, ...bindings.map(binding => binding.sourceId)])) {\n const reference = decodeSourceId(sourceId);\n if (!reference || reference.host) continue;\n const candidates = canonicalByProject.get(reference.projectId) ?? [];\n if (candidates.length === 1) migrations.set(sourceId, candidates[0]!);\n }\n return intake.migrateSourceIds({\n orgId,\n integrationId: 'gitlab',\n migrations: [...migrations].map(([from, to]) => ({ from, to })),\n });\n}\n\nexport function buildGitLabRoutes(options: BuildGitLabRoutesOptions): ApiRoute[] {\n const routes: ApiRoute[] = [];\n const { gitlab, auth, intake, emitAudit } = options;\n const enabled = Boolean(gitlab && auth?.enabled());\n\n if (gitlab && auth) {\n routes.push(\n registerApiRoute('/web/gitlab/status', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const diagnostics = gitlab.diagnostics();\n if (!enabled) {\n return c.json({ enabled: false, configured: true, reauthRequired: false, reason: 'missing_config' });\n }\n await auth.ensureUser(loose(c));\n const tenant = auth.tenant(loose(c));\n if (!tenant) return c.json({ error: 'unauthorized', reason: 'auth_required' }, 401);\n if (!tenant.orgId) {\n return c.json({\n enabled: true,\n configured: false,\n organizationRequired: true,\n connections: [],\n accounts: [],\n reauthRequired: false,\n reason: 'organization_required',\n });\n }\n\n try {\n await gitlab.verifyStatus();\n const connections = await gitlab.statusConnections();\n const active = connections?.filter(connection => connection.status === 'active');\n const direct = connections === undefined;\n const endpointHost = typeof diagnostics.endpointHost === 'string' ? diagnostics.endpointHost : null;\n const accounts = direct\n ? endpointHost\n ? [endpointHost]\n : []\n : (active ?? []).flatMap(connection => (connection.accountLabel ? [connection.accountLabel] : []));\n const configured = direct || (active?.length ?? 0) > 0;\n return c.json({\n enabled: true,\n configured,\n mode: direct ? 'direct' : 'platform',\n ...(connections ? { connections } : {}),\n accounts,\n reauthRequired: connections?.some(connection => connection.status === 'needs_reauth') ?? false,\n reason: configured ? 'ready' : 'not_connected',\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n registerApiRoute('/web/gitlab/projects', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n try {\n const sources = await gitlab.intake.listSources(resolved.tenant);\n if (intake) {\n const identity = await reconcileGitLabSourceIdentity({\n intake,\n orgId: resolved.tenant.orgId,\n sources,\n });\n if (identity.conflicts.length > 0) {\n return c.json(\n {\n error: 'gitlab_source_identity_conflict',\n message: 'Conflicting routes exist for the same GitLab project. Resolve the duplicate bindings.',\n conflicts: identity.conflicts,\n },\n 409,\n );\n }\n if (identity.migrations.length > 0) {\n await emitAudit?.({\n context: loose(c),\n input: {\n action: 'factory.intake.config_updated',\n targets: identity.migrations.map(migration => ({ type: 'intake_source', id: migration.to })),\n metadata: { provider: 'gitlab', migrated: identity.migrations.length },\n },\n });\n }\n }\n return c.json({\n projects: sources.flatMap(source => {\n const project = gitlabProjectPayload(source);\n return project ? [{ ...project, sandboxProvider: options.sandbox ? 'custom' : 'none' }] : [];\n }),\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n registerApiRoute('/web/gitlab/projects/registration', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const input = (await c.req.json().catch(() => null)) as { sourceId?: unknown } | null;\n const sourceId = typeof input?.sourceId === 'string' ? input.sourceId.trim() : '';\n if (!sourceId) return c.json({ error: 'invalid_gitlab_source' }, 400);\n try {\n const source = (await gitlab.intake.listSources(resolved.tenant)).find(\n candidate => candidate.id === sourceId,\n );\n const project = source ? gitlabProjectPayload(source) : null;\n if (!project) return c.json({ error: 'gitlab_project_not_found' }, 404);\n const installation = await gitlab.versionControl.registerInstallation({\n orgId: resolved.tenant.orgId,\n userId: resolved.tenant.userId,\n installation: {\n externalId: project.connectionId,\n accountName: project.accountLabel ?? 'GitLab',\n accountType: 'GitLab',\n metadata: { connection: gitlabConnection(project.connectionId) },\n },\n });\n return c.json({\n project: {\n ...project,\n installationStorageId: installation.id,\n sandboxProvider: options.sandbox ? 'custom' : 'none',\n },\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n registerApiRoute('/web/gitlab/projects/:id/prs', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const factoryProjectId = c.req.query('factoryProjectId')?.trim();\n const page = positiveId(c.req.query('page') ?? '1');\n if (!factoryProjectId || !page) return c.json({ error: 'invalid_request' }, 400);\n if ((await gitlab.resolveOrgId(factoryProjectId)) !== resolved.tenant.orgId)\n return c.json({ error: 'factory_project_not_found' }, 404);\n const linked = await gitlab.getLinkedRepository({\n orgId: resolved.tenant.orgId,\n factoryProjectId,\n projectRepositoryId: c.req.param('id'),\n });\n if (!linked) return c.json({ error: 'project_repository_not_found' }, 404);\n const projectId = positiveId(linked.repository.externalId);\n if (!projectId) return c.json({ error: 'invalid_gitlab_project' }, 502);\n try {\n const target = await gitlab.versionControl.getRepositoryTarget({\n orgId: resolved.tenant.orgId,\n repositoryId: linked.repository.id,\n });\n const result = await gitlab.versionControl.listPullRequests({\n ...target,\n includeDrafts: false,\n cursor: String(page),\n });\n return c.json({\n pullRequests: result.pullRequests.map(pr => ({\n number: Number(pr.id),\n externalId: mergeRequestSourceKey(linked.host, projectId, Number(pr.id)),\n title: pr.title,\n url: pr.url,\n author: pr.author,\n assignees: pr.assignees ?? [],\n requestedReviewers: pr.requestedReviewers ?? [],\n labels: pr.labels ?? [],\n baseBranch: pr.baseBranch,\n headBranch: pr.headBranch,\n createdAt: pr.createdAt,\n updatedAt: pr.updatedAt,\n })),\n nextPage: result.nextCursor === null ? null : Number(result.nextCursor),\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n registerApiRoute('/web/gitlab/projects/:id/prs/:number', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const factoryProjectId = c.req.query('factoryProjectId')?.trim();\n const number = positiveId(c.req.param('number'));\n if (!factoryProjectId || !number) return c.json({ error: 'invalid_request' }, 400);\n if ((await gitlab.resolveOrgId(factoryProjectId)) !== resolved.tenant.orgId)\n return c.json({ error: 'factory_project_not_found' }, 404);\n const linked = await gitlab.getLinkedRepository({\n orgId: resolved.tenant.orgId,\n factoryProjectId,\n projectRepositoryId: c.req.param('id'),\n });\n if (!linked) return c.json({ error: 'project_repository_not_found' }, 404);\n try {\n const target = await gitlab.versionControl.getRepositoryTarget({\n orgId: resolved.tenant.orgId,\n repositoryId: linked.repository.id,\n });\n const pr = await gitlab.versionControl.getPullRequest({ ...target, pullRequestId: String(number) });\n if (!pr) return c.json({ error: 'merge_request_not_found' }, 404);\n return c.json({\n number,\n title: pr.title,\n url: pr.url,\n author: pr.author,\n assignees: pr.assignees ?? [],\n requestedReviewers: pr.requestedReviewers ?? [],\n baseBranch: pr.baseBranch,\n headBranch: pr.headBranch,\n createdAt: pr.createdAt,\n updatedAt: pr.updatedAt,\n description: pr.body,\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n );\n }\n\n if (gitlab && auth && intake) {\n routes.push(\n registerApiRoute('/web/gitlab/issues', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const factoryProjectId = c.req.query('factoryProjectId')?.trim();\n if (!factoryProjectId) return c.json({ error: 'invalid_factory_project_id' }, 400);\n const board = c.req.query('board')?.trim();\n if (!board) return c.json({ error: 'invalid_board' }, 400);\n if ((await gitlab.resolveOrgId(factoryProjectId)) !== resolved.tenant.orgId) {\n return c.json({ error: 'factory_project_not_found' }, 404);\n }\n\n await intake.ensureReady();\n const config = await intake.getConfig({ orgId: resolved.tenant.orgId, integrationIds: ['gitlab'] });\n const selection = config.gitlab!;\n if (!selection.enabled) {\n return c.json(\n { error: 'gitlab_intake_disabled', message: 'GitLab intake is turned off in Settings.' },\n 404,\n );\n }\n const selected = new Set(selection.sourceIds ?? []);\n const sourceIds = [\n ...new Set(\n (await intake.listBindings({ orgId: resolved.tenant.orgId, integrationId: 'gitlab' }))\n .filter(\n binding =>\n binding.factoryProjectId === factoryProjectId &&\n binding.board === board &&\n selected.has(binding.sourceId),\n )\n .map(binding => binding.sourceId),\n ),\n ];\n if (sourceIds.length === 0) return c.json({ issues: [], nextCursor: null });\n\n try {\n const result = await gitlab.intake.listIssues({\n connection: { type: 'oauth', accessToken: 'gitlab-route' },\n sourceIds,\n cursor: c.req.query('after')?.trim() || undefined,\n });\n return c.json({\n ...result,\n issues: result.issues.map(issue => {\n const source = decodeSourceId(issue.sourceId ?? '');\n const issueIid = Number(issue.id);\n if (!source || !Number.isSafeInteger(issueIid) || issueIid <= 0) {\n throw new Error('GitLab returned an invalid routed issue reference.');\n }\n return { ...issue, externalId: encodeIssueReference({ ...source, issueIid }) };\n }),\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n );\n\n routes.push(\n registerApiRoute('/web/gitlab/issues/:issueId', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const factoryProjectId = c.req.query('factoryProjectId')?.trim();\n if (!factoryProjectId) return c.json({ error: 'invalid_factory_project_id' }, 400);\n if ((await gitlab.resolveOrgId(factoryProjectId)) !== resolved.tenant.orgId) {\n return c.json({ error: 'factory_project_not_found' }, 404);\n }\n const issueId = c.req.param('issueId')?.trim();\n const issueReference = issueId ? decodeIssueReference(issueId) : null;\n if (!issueReference) return c.json({ error: 'invalid_gitlab_issue_id' }, 400);\n\n const { host, connectionId, projectId, projectPath } = issueReference;\n const sourceId = host\n ? encodeSourceId({ host, projectId })\n : encodeSourceId({ connectionId, projectId, projectPath });\n await intake.ensureReady();\n const config = await intake.getConfig({ orgId: resolved.tenant.orgId, integrationIds: ['gitlab'] });\n const selection = config.gitlab!;\n const selected = new Set(selection.sourceIds ?? []);\n const routed =\n selection.enabled &&\n selected.has(sourceId) &&\n (await intake.listBindings({ orgId: resolved.tenant.orgId, integrationId: 'gitlab' })).some(\n binding => binding.factoryProjectId === factoryProjectId && binding.sourceId === sourceId,\n );\n if (!routed) return c.json({ error: 'gitlab_issue_not_routed' }, 404);\n\n try {\n const issue = await gitlab.intake.getIssue({\n connection: { type: 'oauth', accessToken: 'gitlab-route' },\n issueId,\n });\n if (!issue) return c.json({ error: 'gitlab_issue_not_found' }, 404);\n return c.json(issue);\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n );\n }\n\n if (gitlab && auth) {\n routes.push(\n registerApiRoute('/web/gitlab/subscriptions', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await auth.ensureUser(loose(c));\n const tenant = auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n // Sessions read by session id; resourceId is the older address.\n const sessionId = c.req.query('sessionId');\n const resourceId = c.req.query('resourceId');\n const threadId = c.req.query('threadId');\n const target = !threadId\n ? null\n : sessionId\n ? { orgId: tenant.orgId, sessionId, threadId }\n : resourceId\n ? { orgId: tenant.orgId, resourceId, threadId }\n : null;\n if (!target) return c.json({ error: 'sessionId (or resourceId) and threadId are required' }, 400);\n\n const subscriptions = await listMergeRequestSubscriptionsForThread(target, gitlab.integrationStorage);\n return c.json({\n subscriptions: subscriptions.map(subscription => ({\n id: subscription.id,\n repoFullName: subscription.data.projectPath,\n pullRequestNumber: Number(subscription.data.changeRequestId),\n status: subscription.status,\n url: mergeRequestUrl(\n subscription.data.host,\n subscription.data.projectPath,\n subscription.data.changeRequestId,\n ),\n })),\n });\n },\n }),\n );\n }\n\n routes.push(\n registerApiRoute('/web/gitlab/webhook', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const result = await handleGitLabWebhook(loose(c), {\n webhookSecret: options.webhookSecret,\n ingestFactoryEvent: options.ingestFactoryEvent,\n ...(options.controller && gitlab\n ? {\n controller: options.controller,\n gitlab,\n onTargetError: (subscription, error) => {\n console.warn(\n `[GitLab Webhook] Delivery failed for subscription ${subscription.id} (${subscription.resourceId}/${subscription.threadId}).`,\n error,\n );\n },\n }\n : {}),\n });\n return c.json(result.body, result.status);\n },\n }),\n );\n return routes;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","names":[],"sources":["../../../src/integrations/gitlab/routes.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { FactorySandbox } from '@mastra/core/workspace';\nimport type { Context } from 'hono';\nimport type { IntakeIssue } from '../../capabilities/intake.js';\nimport type { PullRequest } from '../../capabilities/version-control.js';\nimport type { RouteAuth } from '../../routes/route.js';\nimport { sanitizeSegment } from '../../sandbox/workdir.js';\nimport type { AuditEmitter } from '../../storage/domains/audit/domain.js';\nimport type { IntakeStorage } from '../../storage/domains/intake/base.js';\nimport { GitLabApiError } from './api.js';\nimport {\n decodeIssueReference,\n decodeSourceId,\n encodeIssueReference,\n encodeSourceId,\n gitlabConnection,\n} from './integration.js';\nimport type { GitLabIntegrationBase } from './integration.js';\nimport { listMergeRequestSubscriptionsForThread, mergeRequestUrl } from './subscriptions.js';\nimport { handleGitLabWebhook } from './webhook.js';\nimport type { GitLabWebhookHandlerOptions, ParsedGitLabWebhook } from './webhook.js';\n\ntype RouteContext = Context;\n\nfunction loose(c: unknown): RouteContext {\n return c as RouteContext;\n}\n\nexport interface BuildGitLabRoutesOptions {\n gitlab?: GitLabIntegrationBase;\n auth?: RouteAuth;\n intake?: IntakeStorage;\n sandbox?: FactorySandbox;\n emitAudit?: AuditEmitter['emit'];\n webhookSecret?: string;\n ingestFactoryEvent?: (event: ParsedGitLabWebhook) => Promise<unknown>;\n /** Mounted agent controller; enables webhook delivery to subscribed sessions. */\n controller?: GitLabWebhookHandlerOptions['controller'];\n}\n\nasync function resolveOrgTenant(\n c: RouteContext,\n auth: RouteAuth,\n): Promise<{ tenant: { orgId: string; userId: string } } | { response: Response }> {\n await auth.ensureUser(c);\n const tenant = auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized', reason: 'auth_required' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n {\n error: 'organization_required',\n message: 'GitLab intake requires an organization. Personal accounts cannot use GitLab intake.',\n },\n 403,\n ),\n };\n }\n return { tenant: { orgId: tenant.orgId, userId: tenant.userId } };\n}\n\nfunction gitlabFetchError(c: RouteContext, error: unknown) {\n if (error instanceof GitLabApiError && error.code === 'gitlab_auth_failed') {\n return c.json({ error: 'gitlab_auth_failed', message: error.message }, 409);\n }\n return c.json({ error: 'gitlab_fetch_failed', message: error instanceof Error ? error.message : String(error) }, 502);\n}\n\ntype GitLabSourceIdentityMigration = { from: string; to: string };\n\nfunction gitlabProjectPayload(source: { id: string; name: string; metadata?: Record<string, unknown> }) {\n const connectionId = typeof source.metadata?.connectionId === 'string' ? source.metadata.connectionId : null;\n const projectId = typeof source.metadata?.projectId === 'string' ? source.metadata.projectId : null;\n const projectPath = typeof source.metadata?.projectPath === 'string' ? source.metadata.projectPath : source.name;\n if (!connectionId || !projectId) return null;\n const repositoryName = projectPath.split('/').filter(Boolean).at(-1) ?? 'repo';\n return {\n id: source.id,\n name: source.name,\n projectId,\n projectPath,\n connectionId,\n accountLabel: typeof source.metadata?.accountLabel === 'string' ? source.metadata.accountLabel : null,\n defaultBranch: typeof source.metadata?.defaultBranch === 'string' ? source.metadata.defaultBranch : 'main',\n sandboxProvider: 'none',\n sandboxWorkdir: `~/${sanitizeSegment(repositoryName)}`,\n };\n}\n\nfunction positiveId(value: string | undefined): number | null {\n const number = Number(value);\n return value && /^[1-9]\\d*$/.test(value) && Number.isSafeInteger(number) ? number : null;\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\nconst LINKED_BACKLOG_MAX_PAGES = 5;\nconst POLLED_ACTOR = '__factory_poll__';\n\nfunction listedMergeRequestEvent(\n project: { host: string; projectId: number; projectPath: string },\n pr: PullRequest,\n): ParsedGitLabWebhook {\n const iid = Number(pr.id);\n return {\n event: 'Merge Request Hook',\n deliveryId: `poll:merge-request:${project.host}:${project.projectId}:${iid}:${pr.createdAt}`,\n instanceHost: project.host,\n payload: {\n user_username: POLLED_ACTOR,\n project: { id: project.projectId, path_with_namespace: project.projectPath, web_url: `https://${project.host}/${project.projectPath}` },\n object_attributes: {\n iid,\n action: 'open',\n state: 'opened',\n title: pr.title,\n url: pr.url,\n created_at: pr.createdAt,\n updated_at: pr.updatedAt,\n source_branch: pr.headBranch,\n target_branch: pr.baseBranch,\n draft: pr.draft,\n author: pr.author ? { username: pr.author } : undefined,\n assignees: (pr.assignees ?? []).map(username => ({ username })),\n reviewers: (pr.requestedReviewers ?? []).map(username => ({ username })),\n labels: pr.labels ?? [],\n },\n },\n };\n}\n\nfunction listedMergeRequestEvents(\n project: { host: string; projectId: number; projectPath: string },\n pr: PullRequest,\n): ParsedGitLabWebhook[] {\n const opened = listedMergeRequestEvent(project, pr);\n if (!pr.headSha || pr.updatedAt === pr.createdAt) return [opened];\n return [\n opened,\n {\n ...opened,\n deliveryId: `poll:merge-request-head:${project.host}:${project.projectId}:${pr.id}:${pr.updatedAt}:${pr.headSha}`,\n payload: {\n ...opened.payload,\n object_attributes: {\n ...(opened.payload.object_attributes as Record<string, unknown>),\n action: 'update',\n last_commit: { id: pr.headSha },\n },\n },\n },\n ];\n}\n\nfunction listedIssueEvent(issue: IntakeIssue): ParsedGitLabWebhook | null {\n const source = decodeSourceId(issue.sourceId ?? '');\n const projectId = source && positiveId(source.projectId);\n const iid = positiveId(issue.id);\n const projectPath = issue.source || source?.projectPath;\n if (!source?.host || !projectId || !iid || !projectPath) return null;\n return {\n event: 'Issue Hook',\n deliveryId: `poll:issue:${source.host}:${projectId}:${iid}:${issue.createdAt}`,\n instanceHost: source.host,\n payload: {\n user_username: POLLED_ACTOR,\n project: { id: projectId, path_with_namespace: projectPath, web_url: `https://${source.host}/${projectPath}` },\n object_attributes: {\n iid,\n action: 'open',\n state: 'opened',\n title: issue.title,\n url: issue.url,\n created_at: issue.createdAt,\n updated_at: issue.updatedAt,\n author: issue.authorUsername ? { username: issue.authorUsername } : undefined,\n labels: issue.labels ?? [],\n },\n },\n };\n}\n\nasync function ingestListedEvents(events: Array<ParsedGitLabWebhook | null>, ingest?: BuildGitLabRoutesOptions['ingestFactoryEvent']) {\n if (!ingest) return;\n const listed = events.filter((event): event is ParsedGitLabWebhook => event !== null);\n const openings = listed.filter(event => !event.deliveryId.startsWith('poll:merge-request-head:'));\n const headUpdates = listed.filter(event => event.deliveryId.startsWith('poll:merge-request-head:'));\n for (const batch of [openings, headUpdates]) {\n const results = await Promise.allSettled(batch.map(event => ingest(event)));\n const rejected = results.find((result): result is PromiseRejectedResult => result.status === 'rejected');\n if (rejected) console.warn('[Mastra Factory] Failed to ingest listed GitLab events', { error: rejected.reason });\n }\n}\n\nexport async function ingestLinkedGitLabRepositoryBacklog(args: {\n gitlab: GitLabIntegrationBase;\n orgId: string;\n projectRepositoryId: string;\n ingestFactoryEvent?: BuildGitLabRoutesOptions['ingestFactoryEvent'];\n}): Promise<void> {\n if (!args.ingestFactoryEvent) return;\n const storage = args.gitlab.sourceControlStorage;\n const link = await storage?.projectRepositories.get({ orgId: args.orgId, id: args.projectRepositoryId });\n if (!link) return;\n const connection = await storage!.connections.get({ orgId: args.orgId, id: link.connectionId });\n if (!connection || connection.integrationId !== 'gitlab') return;\n const linked = await args.gitlab.getLinkedRepository({\n orgId: args.orgId,\n factoryProjectId: connection.factoryProjectId,\n projectRepositoryId: link.id,\n });\n if (!linked) return;\n const projectId = positiveId(linked.repository.externalId);\n if (!projectId) return;\n const project = { host: linked.host, projectId, projectPath: linked.repository.slug };\n const target = await args.gitlab.versionControl.getRepositoryTarget({\n orgId: args.orgId,\n repositoryId: linked.repository.id,\n });\n for (let page = 1; page <= LINKED_BACKLOG_MAX_PAGES; page += 1) {\n const result = await args.gitlab.versionControl.listPullRequests({ ...target, includeDrafts: false, cursor: String(page) });\n await ingestListedEvents(result.pullRequests.flatMap(pr => listedMergeRequestEvents(project, pr)), args.ingestFactoryEvent);\n if (result.nextCursor === null) break;\n }\n const sourceId = encodeSourceId({ host: linked.host, projectId: String(projectId) });\n let cursor: string | undefined;\n for (let page = 1; page <= LINKED_BACKLOG_MAX_PAGES; page += 1) {\n const result = await args.gitlab.intake.listIssues({ connection: target.connection, sourceIds: [sourceId], cursor });\n await ingestListedEvents(result.issues.map(listedIssueEvent), args.ingestFactoryEvent);\n if (result.nextCursor === null) break;\n cursor = result.nextCursor;\n }\n}\n\nasync function reconcileGitLabSourceIdentity({\n intake,\n orgId,\n sources,\n}: {\n intake: IntakeStorage;\n orgId: string;\n sources: Array<{ id: string; metadata?: Record<string, unknown> }>;\n}): Promise<{ migrations: GitLabSourceIdentityMigration[]; conflicts: GitLabSourceIdentityMigration[] }> {\n await intake.ensureReady();\n const [config, bindings] = await Promise.all([\n intake.getConfig({ orgId }),\n intake.listBindings({ orgId, integrationId: 'gitlab' }),\n ]);\n const selected = config.gitlab?.sourceIds ?? [];\n const canonicalByProject = new Map<string, string[]>();\n for (const source of sources) {\n const reference = decodeSourceId(source.id);\n if (!reference?.host) continue;\n const ids = canonicalByProject.get(reference.projectId) ?? [];\n ids.push(source.id);\n canonicalByProject.set(reference.projectId, ids);\n }\n\n const migrations = new Map<string, string>();\n for (const sourceId of new Set([...selected, ...bindings.map(binding => binding.sourceId)])) {\n const reference = decodeSourceId(sourceId);\n if (!reference || reference.host) continue;\n const candidates = canonicalByProject.get(reference.projectId) ?? [];\n if (candidates.length === 1) migrations.set(sourceId, candidates[0]!);\n }\n return intake.migrateSourceIds({\n orgId,\n integrationId: 'gitlab',\n migrations: [...migrations].map(([from, to]) => ({ from, to })),\n });\n}\n\nexport function buildGitLabRoutes(options: BuildGitLabRoutesOptions): ApiRoute[] {\n const routes: ApiRoute[] = [];\n const { gitlab, auth, intake, emitAudit } = options;\n const enabled = Boolean(gitlab && auth?.enabled());\n\n if (gitlab && auth) {\n routes.push(\n registerApiRoute('/web/gitlab/status', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const diagnostics = gitlab.diagnostics();\n if (!enabled) {\n return c.json({ enabled: false, configured: true, reauthRequired: false, reason: 'missing_config' });\n }\n await auth.ensureUser(loose(c));\n const tenant = auth.tenant(loose(c));\n if (!tenant) return c.json({ error: 'unauthorized', reason: 'auth_required' }, 401);\n if (!tenant.orgId) {\n return c.json({\n enabled: true,\n configured: false,\n organizationRequired: true,\n connections: [],\n accounts: [],\n reauthRequired: false,\n reason: 'organization_required',\n });\n }\n\n try {\n await gitlab.verifyStatus();\n const connections = await gitlab.statusConnections();\n const active = connections?.filter(connection => connection.status === 'active');\n const direct = connections === undefined;\n const endpointHost = typeof diagnostics.endpointHost === 'string' ? diagnostics.endpointHost : null;\n const accounts = direct\n ? endpointHost\n ? [endpointHost]\n : []\n : (active ?? []).flatMap(connection => (connection.accountLabel ? [connection.accountLabel] : []));\n const configured = direct || (active?.length ?? 0) > 0;\n return c.json({\n enabled: true,\n configured,\n mode: direct ? 'direct' : 'platform',\n ...(connections ? { connections } : {}),\n accounts,\n reauthRequired: connections?.some(connection => connection.status === 'needs_reauth') ?? false,\n reason: configured ? 'ready' : 'not_connected',\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n registerApiRoute('/web/gitlab/projects', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n try {\n const sources = await gitlab.intake.listSources(resolved.tenant);\n if (intake) {\n const identity = await reconcileGitLabSourceIdentity({\n intake,\n orgId: resolved.tenant.orgId,\n sources,\n });\n if (identity.conflicts.length > 0) {\n return c.json(\n {\n error: 'gitlab_source_identity_conflict',\n message: 'Conflicting routes exist for the same GitLab project. Resolve the duplicate bindings.',\n conflicts: identity.conflicts,\n },\n 409,\n );\n }\n if (identity.migrations.length > 0) {\n await emitAudit?.({\n context: loose(c),\n input: {\n action: 'factory.intake.config_updated',\n targets: identity.migrations.map(migration => ({ type: 'intake_source', id: migration.to })),\n metadata: { provider: 'gitlab', migrated: identity.migrations.length },\n },\n });\n }\n }\n return c.json({\n projects: sources.flatMap(source => {\n const project = gitlabProjectPayload(source);\n return project ? [{ ...project, sandboxProvider: options.sandbox ? 'custom' : 'none' }] : [];\n }),\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n registerApiRoute('/web/gitlab/projects/registration', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const input = (await c.req.json().catch(() => null)) as { sourceId?: unknown } | null;\n const sourceId = typeof input?.sourceId === 'string' ? input.sourceId.trim() : '';\n if (!sourceId) return c.json({ error: 'invalid_gitlab_source' }, 400);\n try {\n const source = (await gitlab.intake.listSources(resolved.tenant)).find(\n candidate => candidate.id === sourceId,\n );\n const project = source ? gitlabProjectPayload(source) : null;\n if (!project) return c.json({ error: 'gitlab_project_not_found' }, 404);\n const installation = await gitlab.versionControl.registerInstallation({\n orgId: resolved.tenant.orgId,\n userId: resolved.tenant.userId,\n installation: {\n externalId: project.connectionId,\n accountName: project.accountLabel ?? 'GitLab',\n accountType: 'GitLab',\n metadata: { connection: gitlabConnection(project.connectionId) },\n },\n });\n return c.json({\n project: {\n ...project,\n installationStorageId: installation.id,\n sandboxProvider: options.sandbox ? 'custom' : 'none',\n },\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n registerApiRoute('/web/gitlab/projects/:id/prs', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const factoryProjectId = c.req.query('factoryProjectId')?.trim();\n const page = positiveId(c.req.query('page') ?? '1');\n if (!factoryProjectId || !page) return c.json({ error: 'invalid_request' }, 400);\n if ((await gitlab.resolveOrgId(factoryProjectId)) !== resolved.tenant.orgId)\n return c.json({ error: 'factory_project_not_found' }, 404);\n const linked = await gitlab.getLinkedRepository({\n orgId: resolved.tenant.orgId,\n factoryProjectId,\n projectRepositoryId: c.req.param('id'),\n });\n if (!linked) return c.json({ error: 'project_repository_not_found' }, 404);\n const projectId = positiveId(linked.repository.externalId);\n if (!projectId) return c.json({ error: 'invalid_gitlab_project' }, 502);\n try {\n const target = await gitlab.versionControl.getRepositoryTarget({\n orgId: resolved.tenant.orgId,\n repositoryId: linked.repository.id,\n });\n const result = await gitlab.versionControl.listPullRequests({\n ...target,\n includeDrafts: false,\n cursor: String(page),\n });\n await ingestListedEvents(\n result.pullRequests.flatMap(pr =>\n listedMergeRequestEvents({ host: linked.host, projectId, projectPath: linked.repository.slug }, pr),\n ),\n options.ingestFactoryEvent,\n );\n return c.json({\n pullRequests: result.pullRequests.map(pr => ({\n number: Number(pr.id),\n externalId: mergeRequestSourceKey(linked.host, projectId, Number(pr.id)),\n title: pr.title,\n url: pr.url,\n author: pr.author,\n assignees: pr.assignees ?? [],\n requestedReviewers: pr.requestedReviewers ?? [],\n labels: pr.labels ?? [],\n baseBranch: pr.baseBranch,\n headBranch: pr.headBranch,\n createdAt: pr.createdAt,\n updatedAt: pr.updatedAt,\n })),\n nextPage: result.nextCursor === null ? null : Number(result.nextCursor),\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n registerApiRoute('/web/gitlab/projects/:id/prs/:number', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const factoryProjectId = c.req.query('factoryProjectId')?.trim();\n const number = positiveId(c.req.param('number'));\n if (!factoryProjectId || !number) return c.json({ error: 'invalid_request' }, 400);\n if ((await gitlab.resolveOrgId(factoryProjectId)) !== resolved.tenant.orgId)\n return c.json({ error: 'factory_project_not_found' }, 404);\n const linked = await gitlab.getLinkedRepository({\n orgId: resolved.tenant.orgId,\n factoryProjectId,\n projectRepositoryId: c.req.param('id'),\n });\n if (!linked) return c.json({ error: 'project_repository_not_found' }, 404);\n try {\n const target = await gitlab.versionControl.getRepositoryTarget({\n orgId: resolved.tenant.orgId,\n repositoryId: linked.repository.id,\n });\n const pr = await gitlab.versionControl.getPullRequest({ ...target, pullRequestId: String(number) });\n if (!pr) return c.json({ error: 'merge_request_not_found' }, 404);\n return c.json({\n number,\n title: pr.title,\n url: pr.url,\n author: pr.author,\n assignees: pr.assignees ?? [],\n requestedReviewers: pr.requestedReviewers ?? [],\n baseBranch: pr.baseBranch,\n headBranch: pr.headBranch,\n createdAt: pr.createdAt,\n updatedAt: pr.updatedAt,\n description: pr.body,\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n );\n }\n\n if (gitlab && auth && intake) {\n routes.push(\n registerApiRoute('/web/gitlab/issues', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const factoryProjectId = c.req.query('factoryProjectId')?.trim();\n if (!factoryProjectId) return c.json({ error: 'invalid_factory_project_id' }, 400);\n const board = c.req.query('board')?.trim();\n if (!board) return c.json({ error: 'invalid_board' }, 400);\n if ((await gitlab.resolveOrgId(factoryProjectId)) !== resolved.tenant.orgId) {\n return c.json({ error: 'factory_project_not_found' }, 404);\n }\n\n await intake.ensureReady();\n const config = await intake.getConfig({ orgId: resolved.tenant.orgId, integrationIds: ['gitlab'] });\n const selection = config.gitlab!;\n if (!selection.enabled) {\n return c.json(\n { error: 'gitlab_intake_disabled', message: 'GitLab intake is turned off in Settings.' },\n 404,\n );\n }\n const selected = new Set(selection.sourceIds ?? []);\n const sourceIds = [\n ...new Set(\n (await intake.listBindings({ orgId: resolved.tenant.orgId, integrationId: 'gitlab' }))\n .filter(\n binding =>\n binding.factoryProjectId === factoryProjectId &&\n binding.board === board &&\n selected.has(binding.sourceId),\n )\n .map(binding => binding.sourceId),\n ),\n ];\n if (sourceIds.length === 0) return c.json({ issues: [], nextCursor: null });\n\n try {\n const result = await gitlab.intake.listIssues({\n connection: { type: 'oauth', accessToken: 'gitlab-route' },\n sourceIds,\n cursor: c.req.query('after')?.trim() || undefined,\n });\n await ingestListedEvents(result.issues.map(listedIssueEvent), options.ingestFactoryEvent);\n return c.json({\n ...result,\n issues: result.issues.map(issue => {\n const source = decodeSourceId(issue.sourceId ?? '');\n const issueIid = Number(issue.id);\n if (!source || !Number.isSafeInteger(issueIid) || issueIid <= 0) {\n throw new Error('GitLab returned an invalid routed issue reference.');\n }\n return { ...issue, externalId: encodeIssueReference({ ...source, issueIid }) };\n }),\n });\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n );\n\n routes.push(\n registerApiRoute('/web/gitlab/issues/:issueId', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await resolveOrgTenant(loose(c), auth);\n if ('response' in resolved) return resolved.response;\n const factoryProjectId = c.req.query('factoryProjectId')?.trim();\n if (!factoryProjectId) return c.json({ error: 'invalid_factory_project_id' }, 400);\n if ((await gitlab.resolveOrgId(factoryProjectId)) !== resolved.tenant.orgId) {\n return c.json({ error: 'factory_project_not_found' }, 404);\n }\n const issueId = c.req.param('issueId')?.trim();\n const issueReference = issueId ? decodeIssueReference(issueId) : null;\n if (!issueReference) return c.json({ error: 'invalid_gitlab_issue_id' }, 400);\n\n const { host, connectionId, projectId, projectPath } = issueReference;\n const sourceId = host\n ? encodeSourceId({ host, projectId })\n : encodeSourceId({ connectionId, projectId, projectPath });\n await intake.ensureReady();\n const config = await intake.getConfig({ orgId: resolved.tenant.orgId, integrationIds: ['gitlab'] });\n const selection = config.gitlab!;\n const selected = new Set(selection.sourceIds ?? []);\n const routed =\n selection.enabled &&\n selected.has(sourceId) &&\n (await intake.listBindings({ orgId: resolved.tenant.orgId, integrationId: 'gitlab' })).some(\n binding => binding.factoryProjectId === factoryProjectId && binding.sourceId === sourceId,\n );\n if (!routed) return c.json({ error: 'gitlab_issue_not_routed' }, 404);\n\n try {\n const issue = await gitlab.intake.getIssue({\n connection: { type: 'oauth', accessToken: 'gitlab-route' },\n issueId,\n });\n if (!issue) return c.json({ error: 'gitlab_issue_not_found' }, 404);\n return c.json(issue);\n } catch (error) {\n return gitlabFetchError(loose(c), error);\n }\n },\n }),\n );\n }\n\n if (gitlab && auth) {\n routes.push(\n registerApiRoute('/web/gitlab/subscriptions', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await auth.ensureUser(loose(c));\n const tenant = auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n // Sessions read by session id; resourceId is the older address.\n const sessionId = c.req.query('sessionId');\n const resourceId = c.req.query('resourceId');\n const threadId = c.req.query('threadId');\n const target = !threadId\n ? null\n : sessionId\n ? { orgId: tenant.orgId, sessionId, threadId }\n : resourceId\n ? { orgId: tenant.orgId, resourceId, threadId }\n : null;\n if (!target) return c.json({ error: 'sessionId (or resourceId) and threadId are required' }, 400);\n\n const subscriptions = await listMergeRequestSubscriptionsForThread(target, gitlab.integrationStorage);\n return c.json({\n subscriptions: subscriptions.map(subscription => ({\n id: subscription.id,\n repoFullName: subscription.data.projectPath,\n pullRequestNumber: Number(subscription.data.changeRequestId),\n status: subscription.status,\n url: mergeRequestUrl(\n subscription.data.host,\n subscription.data.projectPath,\n subscription.data.changeRequestId,\n ),\n })),\n });\n },\n }),\n );\n }\n\n routes.push(\n registerApiRoute('/web/gitlab/webhook', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const result = await handleGitLabWebhook(loose(c), {\n webhookSecret: options.webhookSecret,\n ingestFactoryEvent: options.ingestFactoryEvent,\n ...(options.controller && gitlab\n ? {\n controller: options.controller,\n gitlab,\n onTargetError: (subscription, error) => {\n console.warn(\n `[GitLab Webhook] Delivery failed for subscription ${subscription.id} (${subscription.resourceId}/${subscription.threadId}).`,\n error,\n );\n },\n }\n : {}),\n });\n return c.json(result.body, result.status);\n },\n }),\n );\n return routes;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/integrations/gitlab/rules.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAS3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAEV,0BAA0B,EAC3B,MAAM,8CAA8C,CAAC;AAEtD,OAAO,KAAK,EAAe,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC9F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,KAAK,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;AAoHtE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,8BAA8B,CAAC,CAAC,kBAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3F,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACpH,yBAAyB,CACvB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,GAAG,eAAe,EAC/B,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAChC;AAED,UAAU,kBAAkB;IAC1B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,aAAa,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;CACvB;AAOD,qBAAa,WAAW;;IACV,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,kBAAkB;IAElD,MAAM,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,aAAa,CAAA;KAAE,CAAC;CAqiB9E;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,kBAAkB,GAC1B,CAAC,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAYhE"}
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/integrations/gitlab/rules.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAS3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAEV,0BAA0B,EAC3B,MAAM,8CAA8C,CAAC;AAEtD,OAAO,KAAK,EAAe,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC9F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,KAAK,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;AAoHtE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,8BAA8B,CAAC,CAAC,kBAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3F,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACpH,yBAAyB,CACvB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,GAAG,eAAe,EAC/B,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAChC;AAED,UAAU,kBAAkB;IAC1B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,aAAa,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;CACvB;AAOD,qBAAa,WAAW;;IACV,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,kBAAkB;IAElD,MAAM,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,aAAa,CAAA;KAAE,CAAC;CA6iB9E;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,kBAAkB,GAC1B,CAAC,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAYhE"}
@@ -221,6 +221,7 @@ var GitLabRules = class {
221
221
  const lastCommit = object(mergeRequest?.last_commit);
222
222
  const headSha = string(lastCommit?.id)?.toLowerCase();
223
223
  const headAt = timestamp(mergeRequest?.updated_at) ?? timestamp(lastCommit?.timestamp);
224
+ const unchangedListedHead = parsed.deliveryId.startsWith("poll:merge-request-head:") && headAt !== void 0 && reviewItem?.metadata?.pullRequestHeadSha === headSha && (reviewItem?.metadata?.pullRequestHeadAt !== headAt || reviewItem?.metadata?.pullRequestHeadDeliveryId !== parsed.deliveryId);
224
225
  let pushedReviewItem = reviewItem;
225
226
  if (event === "mergeRequestUpdated" && headSha && reviewItem) {
226
227
  const parent = items.find((item) => item.id === reviewItem.parentWorkItemId);
@@ -228,7 +229,8 @@ var GitLabRules = class {
228
229
  orgId: reviewItem.orgId,
229
230
  id: reviewItem.id,
230
231
  headSha,
231
- headAt
232
+ headAt,
233
+ deliveryId: parsed.deliveryId.startsWith("poll:merge-request-head:") ? parsed.deliveryId : void 0
232
234
  });
233
235
  if (parent) await recordPullRequestHead(this.options.storage, {
234
236
  orgId: parent.orgId,
@@ -241,6 +243,7 @@ var GitLabRules = class {
241
243
  orgId: reviewItem.orgId,
242
244
  id: reviewItem.id
243
245
  }) ?? reviewItem;
246
+ if (unchangedListedHead) return { status: "ignored" };
244
247
  }
245
248
  const candidates = event === "mergeRequestMerged" ? [...new Map([reviewItem, authoringItem].filter(Boolean).map((item) => [item.id, item])).values()] : event === "mergeRequestCommented" ? [authoringItem ?? reviewItem] : event === "mergeRequestOpened" ? [authoringItem, ...authoringItem ? [authoringItem] : []] : event.startsWith("mergeRequest") ? [pushedReviewItem ?? authoringItem] : [issueItem];
246
249
  const results = [];
@@ -1 +1 @@
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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"}
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 const unchangedListedHead =\n parsed.deliveryId.startsWith('poll:merge-request-head:') &&\n headAt !== undefined &&\n reviewItem?.metadata?.pullRequestHeadSha === headSha &&\n (reviewItem?.metadata?.pullRequestHeadAt !== headAt ||\n reviewItem?.metadata?.pullRequestHeadDeliveryId !== parsed.deliveryId);\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 deliveryId: parsed.deliveryId.startsWith('poll:merge-request-head:') ? parsed.deliveryId : undefined,\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 if (unchangedListedHead) return { status: 'ignored' };\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":";;;;;;;;AA8BA,MAAM,kBAAkB;AAIxB,SAAS,OAAO,OAAqD;CACnE,OAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAK,QAAoC,KAAA;AAC5G;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ,KAAA;AACjE;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ,KAAA;AACvE;;AAGA,SAAS,UAAU,OAAoC;CACrD,MAAM,OAAO,OAAO,KAAK;CACzB,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,MAAM,SAAS,mFAAmF,KAAK,IAAI;CAC3G,MAAM,SAAS,KAAK,MAClB,SAAS,GAAG,OAAO,GAAG,GAAG,OAAO,KAAK,OAAO,KAAK,GAAG,OAAO,GAAG,GAAG,OAAO,OAAO,QAAQ,IACzF;CACA,OAAO,OAAO,MAAM,MAAM,IAAI,KAAA,IAAY;AAC5C;AAEA,SAAS,QAAQ,OAAqC;CACpD,OAAO,OAAO,UAAU,YAAY,QAAQ,KAAA;AAC9C;AAEA,SAAS,UAAU,OAA0B;CAC3C,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC;CACnC,OAAO,MAAM,SAAQ,UAAS;EAC5B,MAAM,WAAW,OAAO,OAAO,KAAK,CAAC,EAAE,QAAQ;EAC/C,OAAO,WAAW,CAAC,QAAQ,IAAI,CAAC;CAClC,CAAC;AACH;AAEA,SAAS,WAAW,OAA0B;CAC5C,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC;CACnC,OAAO,MAAM,SAAQ,UAAS;EAC5B,IAAI,OAAO,UAAU,UAAU,OAAO,QAAQ,CAAC,KAAK,IAAI,CAAC;EACzD,MAAM,QAAQ,OAAO,OAAO,KAAK,CAAC,EAAE,KAAK,KAAK,OAAO,OAAO,KAAK,CAAC,EAAE,IAAI;EACxE,OAAO,QAAQ,CAAC,KAAK,IAAI,CAAC;CAC5B,CAAC;AACH;AAEA,SAAS,YAAY,OAAwC;CAC3D,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC;CACnC,OAAO,OAAO,YACZ,MAAM,SAAQ,UAAS;EACrB,MAAM,QAAQ,OAAO,KAAK;EAC1B,MAAM,OAAO,OAAO,OAAO,KAAK,KAAK,OAAO,OAAO,IAAI;EACvD,MAAM,QAAQ,OAAO,OAAO,KAAK;EACjC,OAAO,QAAQ,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC;CAC5C,CAAC,CACH;AACF;AAEA,SAAS,UAAU,QAAiE;CAClF,MAAM,aAAa,OAAO,OAAO,QAAQ,iBAAiB;CAC1D,MAAM,SAAS,OAAO,YAAY,MAAM,CAAC,EAAE,YAAY;CACvD,IAAI,OAAO,UAAU,cAAc;EACjC,IAAI,WAAW,UAAU,WAAW,UAAU,OAAO;EACrD,IAAI,WAAW,SAAS,OAAO;EAC/B,OAAO;CACT;CACA,IAAI,OAAO,UAAU,aAAa;EAChC,MAAM,eAAe,OAAO,YAAY,aAAa;EACrD,IAAI,iBAAiB,SAAS,OAAO;EACrC,IAAI,iBAAiB,gBAAgB,OAAO;EAC5C;CACF;CACA,IAAI,OAAO,UAAU,sBAAsB;CAC3C,IAAI,WAAW,UAAU,WAAW,UAAU,OAAO;CACrD,IAAI,WAAW,SAAS,OAAO;CAC/B,IAAI,WAAW,SAAS,OAAO;CAC/B,OAAO;AACT;AAEA,SAAS,cAAc,MAAsB;CAC3C,OAAO,KAAK,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,QAAQ,OAAO,EAAE;AACpD;AAEA,SAAS,gBAAgB,QAAiD;CACxE,MAAM,eAAe,OAAO,eAAe,cAAc,OAAO,YAAY,IAAI,KAAA;CAChF,MAAM,aAAa,OAAO,OAAO,OAAO,QAAQ,OAAO,CAAC,EAAE,OAAO;CACjE,IAAI,CAAC,YAAY,OAAO;CACxB,IAAI;EACF,MAAM,cAAc,cAAc,IAAI,IAAI,UAAU,CAAC,CAAC,IAAI;EAC1D,IAAI,gBAAgB,iBAAiB,aAAa,OAAO,KAAA;EACzD,OAAO;CACT,QAAQ;EACN;CACF;AACF;AAEA,SAAS,sBAAsB,MAAc,WAAmB,iBAAiC;CAC/F,OAAO,aAAa,OAAO,KAAK,KAAK,UAAU;EAAE,SAAS;EAAG;EAAM;EAAW;CAAgB,CAAC,GAAG,MAAM,CAAC,CAAC,SAAS,WAAW;AAChI;AAEA,eAAe,gBAAmB,SAAiC;CACjE,IAAI;CACJ,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CACxB,SACA,IAAI,SAAgB,GAAG,WAAW;GAChC,UAAU,iBAAiB,uBAAO,IAAI,MAAM,sBAAsB,CAAC,GAAG,eAAe;EACvF,CAAC,CACH,CAAC;CACH,UAAU;EACR,IAAI,SAAS,aAAa,OAAO;CACnC;AACF;AAEA,MAAM,0BAA0B;AA6BhC,IAAa,cAAb,MAAyB;CACM;CAA7B,YAAY,SAA8C;EAA7B,KAAA,UAAA;CAA8B;CAE3D,MAAM,OAAO,QAAiE;EAC5E,MAAM,QAAQ,UAAU,MAAM;EAC9B,MAAM,UAAU,OAAO,OAAO,QAAQ,OAAO;EAC7C,MAAM,YAAY,OAAO,SAAS,EAAE;EACpC,MAAM,cAAc,OAAO,SAAS,mBAAmB;EACvD,MAAM,OAAO,gBAAgB,MAAM;EACnC,MAAM,WAAW,OAAO,OAAO,QAAQ,aAAa,KAAK,OAAO,OAAO,OAAO,QAAQ,IAAI,CAAC,EAAE,QAAQ;EACrG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,eAAe,CAAC,QAAQ,CAAC,UAAU,OAAO,EAAE,QAAQ,UAAU;EAE3F,MAAM,UAAU,MAAM,KAAK,SAAS,OAAO,SAAS,GAAG,IAAI;EAC3D,IAAI,QAAQ,WAAW,GAAG,OAAO,EAAE,QAAQ,UAAU;EACrD,MAAM,UAA4C,CAAC;EACnD,KAAK,MAAM,UAAU,SACnB,QAAQ,KAAK,MAAM,KAAK,eAAe,QAAQ,OAAO,WAAW,aAAa,MAAM,UAAU,MAAM,CAAC;EAEvG,IAAI,QAAQ,MAAK,WAAU,OAAO,WAAW,WAAW,GAAG,OAAO,EAAE,QAAQ,YAAY;EACxF,IAAI,QAAQ,MAAK,WAAU,OAAO,WAAW,UAAU,GAAG,OAAO,EAAE,QAAQ,WAAW;EACtF,OAAO,QAAQ,MAAM,EAAE,QAAQ,UAAU;CAC3C;CAEA,MAAM,SAAS,sBAA8B,MAAwC;EACnF,MAAM,0BAAU,IAAI,IAA2B;EAC/C,MAAM,QAAQ,MAAM,KAAK,QAAQ,cAAc,oBAAoB,2BAA2B,EAAA,CAAG,QAC/F,QAAO,IAAI,yBAAyB,oBACtC;EACA,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,UAAU,MAAM,KAAK,QAAQ,cAAc,oBAAoB,yBAAyB;IAC5F,wBAAwB,IAAI;IAC5B;GACF,CAAC;GACD,KAAK,MAAM,UAAU,SAAS;IAK5B,MAAM,mBAAmB,QAAO,MAJL,KAAK,QAAQ,cAAc,cAAc,iBAAiB;KACnF,OAAO,OAAO;KACd,YAAY,IAAI;IAClB,CAAC,EAAA,EAC6C,iBAAiB,IAAI;IACnE,IAAI,CAAC,oBAAoB,cAAc,gBAAgB,MAAM,cAAc,IAAI,GAAG;IAClF,MAAM,KAAK,GAAG,OAAO,MAAM,GAAG,OAAO;IACrC,MAAM,UAAU,QAAQ,IAAI,EAAE;IAC9B,IAAI,SACE;SAAA,CAAC,QAAQ,cAAc,SAAS,IAAI,sBAAsB,GAC5D,QAAQ,cAAc,KAAK,IAAI,sBAAsB;IAAA,OAGvD,QAAQ,IAAI,IAAI;KAAE;KAAQ,eAAe,CAAC,IAAI,sBAAsB;IAAE,CAAC;GAE3E;EACF;EACA,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC;CAC7B;CAEA,MAAM,eACJ,QACA,OACA,WACA,aACA,MACA,UACA,QACoC;EACpC,MAAM,iBAAiB,MAAM,KAAK,QAAQ,SAAS,IAAI;GACrD,OAAO,OAAO,OAAO;GACrB,IAAI,OAAO,OAAO;EACpB,CAAC;EACD,IAAI,CAAC,gBAAgB,OAAO,EAAE,QAAQ,UAAU;EAEhD,MAAM,WAAW,eAAe;GAAE;GAAM,WAAW,OAAO,SAAS;EAAE,CAAC;EACtE,MAAM,SAAS,MAAM,KAAK,QAAQ,OAAO,UAAU;GACjD,OAAO,OAAO,OAAO;GACrB,gBAAgB,CAAC,QAAQ;EAC3B,CAAC;EACD,IAAI,CAAC,OAAO,QAAQ,WAAW,CAAC,OAAO,OAAO,WAAW,SAAS,QAAQ,GAAG,OAAO,EAAE,QAAQ,UAAU;EACxG,MAAM,WACJ,MAAM,KAAK,QAAQ,OAAO,aAAa;GAAE,OAAO,OAAO,OAAO;GAAO,eAAe;EAAS,CAAC,EAAA,CAC9F,MACA,cAAa,UAAU,aAAa,YAAY,UAAU,qBAAqB,OAAO,OAAO,gBAC/F;EACA,IAAI,CAAC,SAAS,OAAO,EAAE,QAAQ,UAAU;EACzC,MAAM,QAAQ,KAAK,QAAQ,OAAO,IAAI,QAAQ,SAAS,MAAM;EAC7D,IAAI,CAAC,OAAO,OAAO,EAAE,QAAQ,UAAU;EAEvC,MAAM,gBAAgB,CACpB,GAAG,IAAI,IACL,MAAM,QAAQ,IACZ,OAAO,cAAc,KACnB,iBAAgB,KAAK,QAAQ,OAAO,iCAAiC,cAAc,IAAI,KAAK,YAC9F,CACF,CACF,CACF;EAEA,MAAM,aAAa,OAAO,OAAO,QAAQ,iBAAiB;EAC1D,MAAM,QAAQ,OAAO,UAAU,cAAc,OAAO,OAAO,QAAQ,KAAK,IAAI;EAC5E,MAAM,eAAe,OAAO,UAAU,cAAc,OAAO,OAAO,QAAQ,aAAa,IAAI;EAC3F,MAAM,WAAW,OAAO,OAAO,GAAG;EAClC,MAAM,kBAAkB,OAAO,cAAc,GAAG;EAChD,MAAM,iBAAiB,WACnB,qBAAqB;GAAE;GAAM,WAAW,OAAO,SAAS;GAAG;EAAS,CAAC,IACrE,KAAA;EACJ,MAAM,kBAAkB,kBAAkB,sBAAsB,MAAM,WAAW,eAAe,IAAI,KAAA;EACpG,MAAM,QAAQ,MAAM,KAAK,QAAQ,QAAQ,KAAK;GAC5C,OAAO,OAAO,OAAO;GACrB,kBAAkB,OAAO,OAAO;EAClC,CAAC;EACD,MAAM,YAAY,iBACd,MAAM,MACJ,SAAQ,KAAK,gBAAgB,kBAAkB,YAAY,KAAK,eAAe,eAAe,cAChG,IACA,KAAA;EACJ,MAAM,aAAa,kBACf,MAAM,MACJ,SAAQ,KAAK,gBAAgB,kBAAkB,YAAY,KAAK,eAAe,eAAe,eAChG,IACA,KAAA;EACJ,MAAM,aAAa,OAAO,cAAc,aAAa,KAAK;EAC1D,MAAM,gBAAgB,aAClB,MAAM,MACJ,SACE,KAAK,gBAAgB,SAAS,kBAC9B,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,MAAK,YAAW,QAAQ,WAAW,UAAU,CAC9E,IACA,KAAA;EACJ,MAAM,eAAe,MAAM,KAAK,SAAS,eAAe,OAAO,SAAS,GAAG,QAAQ;EACnF,MAAM,cAAc,OAAO,OAAO,OAAO,QAAQ,IAAI,CAAC,EAAE,EAAE,KAAK,OAAO,OAAO,QAAQ,OAAO;EAC5F,MAAM,gBAAgB,OACpB,MACA,MACA,QACgC;GAChC,IAAI,CAAC,QAAQ,CAAC,KAAK,OAAO,KAAA;GAC1B,MAAM,WAAW,OAAO,OAAO,KAAK,MAAM,CAAC,EAAE,QAAQ;GACrD,IAAI,UAAU,OAAO;GACrB,IAAI,eAAe,gBAAgB,OAAO,KAAK,SAAS,GAAG,OAAO;GAClE,KAAK,MAAM,gBAAgB,eACzB,IAAI;IACF,MAAM,WAAW,MAAM,KAAK,QAAQ,OAAO,0BACzC,cACA,OAAO,SAAS,GAChB,MACA,GACF;IACA,IAAI,UAAU,OAAO;GACvB,QAAQ,CAER;EAGJ;EACA,MAAM,CAAC,aAAa,sBAAsB,MAAM,QAAQ,IAAI,CAC1D,cAAc,OAAO,SAAS,QAAQ,GACtC,cAAc,cAAc,iBAAiB,eAAe,CAC9D,CAAC;EACD,MAAM,gBAAgB,OAAO,WAAiD;GAC5E,IAAI,CAAC,QAAQ,OAAO;GACpB,OAAO,WAAW,WAAW,eAAe,KAAK,SAAS,eAAe,OAAO,SAAS,GAAG,MAAM;EACpG;EACA,MAAM,CAAC,oBAAoB,6BAA6B,MAAM,QAAQ,IAAI,CACxE,cAAc,WAAW,GACzB,cAAc,kBAAkB,CAClC,CAAC;EACD,MAAM,QAA0B;GAC9B,MAAM;GACN;GACA,SAAS;GACT,iBAAiB;EACnB;EAQA,MAAM,aAAa,OAAO,cAAc,WAAW;EACnD,MAAM,UAAU,OAAO,YAAY,EAAE,CAAC,EAAE,YAAY;EAGpD,MAAM,SAAS,UAAU,cAAc,UAAU,KAAK,UAAU,YAAY,SAAS;EACrF,MAAM,sBACJ,OAAO,WAAW,WAAW,0BAA0B,KACvD,WAAW,KAAA,KACX,YAAY,UAAU,uBAAuB,YAC5C,YAAY,UAAU,sBAAsB,UAC3C,YAAY,UAAU,8BAA8B,OAAO;EAC/D,IAAI,mBAAmB;EACvB,IAAI,UAAU,yBAAyB,WAAW,YAAY;GAC5D,MAAM,SAAS,MAAM,MAAK,SAAQ,KAAK,OAAO,WAAW,gBAAgB;GAGzE,MAAM,sBAAsB,KAAK,QAAQ,SAAS;IAChD,OAAO,WAAW;IAClB,IAAI,WAAW;IACf;IACA;IACA,YAAY,OAAO,WAAW,WAAW,0BAA0B,IAAI,OAAO,aAAa,KAAA;GAC7F,CAAC;GACD,IAAI,QACF,MAAM,sBAAsB,KAAK,QAAQ,SAAS;IAChD,OAAO,OAAO;IACd,IAAI,OAAO;IACX;IACA;IACA,wBAAwB;GAC1B,CAAC;GAEH,mBAAoB,MAAM,KAAK,QAAQ,QAAQ,IAAI;IAAE,OAAO,WAAW;IAAO,IAAI,WAAW;GAAG,CAAC,KAAM;GACvG,IAAI,qBAAqB,OAAO,EAAE,QAAQ,UAAU;EACtD;EACA,MAAM,aACJ,UAAU,uBACN,CAAC,GAAG,IAAI,IAAI,CAAC,YAAY,aAAa,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAI,SAAQ,CAAC,KAAM,IAAI,IAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAChG,UAAU,0BACR,CAAC,iBAAiB,UAAU,IAC5B,UAAU,uBACR,CAAC,eAAe,GAAI,gBAAgB,CAAC,aAAa,IAAI,CAAC,CAAE,IACzD,MAAM,WAAW,cAAc,IAC7B,CAAC,oBAAoB,aAAa,IAClC,CAAC,SAAS;EACtB,MAAM,UAAU,CAAC;EACjB,KAAK,MAAM,CAAC,OAAO,SAAS,WAAW,QAAQ,GAC7C,QAAQ,KACN,MAAM,KAAK,UAAU;GACnB;GACA;GACA;GACA;GACA;GACA;GACA,OAAO,OAAO,OAAO;GACrB,kBAAkB,OAAO,OAAO;GAChC;GACA,OAAO;IAAE,OAAO,MAAM;IAAI,cAAc,MAAM;GAAa;GAC3D;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,iBAAiB,QAAQ,aAAa;GACtC;GACA,iBAAiB,UAAU,UAAU,GAAG,OAAO,aAAa,QAAQ,IAAI,MAAM,MAAM,UAAU;GAM9F,GAAI,UAAU,uBAAuB,EAAE,yBAAyB,QAAS,UAAqB,SAAmB,IAAI,CAAC;EACxH,CAAC,CACH;EAMF,MAAM,eAAe,aACjB,MAAM,QACJ,SACE,KAAK,gBAAgB,SAAS,kBAC9B,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,MAAK,YAAW,QAAQ,WAAW,UAAU,CAC9E,IACA,CAAC;EACL,IAAI,iBAAiB,mBAAmB,aAAa,WAAW,GAC9D,MAAM,KAAK,uBAAuB;GAChC,OAAO,cAAc;GACrB,IAAI,cAAc;GAClB;GACA;GACA,SAAS,UAAU,cAAc,UAAU;GAC3C,QAAQ,OAAO,cAAc,KAAK,MAAM,aAAa,gBAAgB;GACrE,SAAS,OAAO,OAAO,cAAc,WAAW,CAAC,EAAE,EAAE,CAAC,EAAE,YAAY;EACtE,CAAC;EAEH,IAAI,QAAQ,MAAK,WAAU,OAAO,WAAW,WAAW,GAAG,OAAO,EAAE,QAAQ,YAAY;EACxF,IAAI,QAAQ,MAAK,WAAU,OAAO,WAAW,UAAU,GAAG,OAAO,EAAE,QAAQ,WAAW;EACtF,OAAO,QAAQ,MAAM,EAAE,QAAQ,UAAU;CAC3C;;;;;;;CAQA,MAAM,uBAAuB,OAQX;EAChB,MAAM,WAAW,MAAM,UAAU,wBAAwB,MAAM,UAAU;EACzE,IAAI,MAAM,UAAU,wBAAwB,CAAC,UAAU;EACvD,MAAM,MAAM,OAAO,MAAM,eAAe;EACxC,KAAK,IAAI,UAAU,GAAG,UAAU,GAAG,WAAW;GAC5C,MAAM,OAAO,MAAM,KAAK,QAAQ,QAAQ,IAAI;IAAE,OAAO,MAAM;IAAO,IAAI,MAAM;GAAG,CAAC;GAChF,IAAI,CAAC,MAAM;GACX,MAAM,SAAS,OAAO,KAAK,UAAU,kBAAkB,KAAK,CAAC;GAC7D,MAAM,OAAO,OAAO,OAAO,IAAI;GAC/B,MAAM,SAAS,OAAO,MAAM,EAAE;GAC9B,IAAI,MAAM,YAAY,KAAA,KAAa,WAAW,KAAA,KAAa,UAAU,MAAM,SAAS;GAEpF,IAAI,MAAM,YAAY,KAAA,KAAa,CAAC,YAAY,MAAM,SAAS,OAAO;GACtE,IAAI,CAAC,YAAY,CAAC,MAAM,QAAQ;GAChC,MAAM,WAAW,KAAK,UAAU;GAChC,MAAM,QAAiC,EACrC,oBAAoB;IAAE,GAAG;KAAS,MAAM;KAAE,IAAI,MAAM,WAAW,UAAU;KAAM,MAAM,CAAC;IAAS;GAAE,EACnG;GACA,IAAI,CAAC,UAAU;IAIb,IAAI,aAAa,MAAM,iBAAiB;KACtC,MAAM,qBAAqB,MAAM,WAAW;KAC5C,MAAM,oBAAoB,MAAM,WAAW;IAC7C;IACA,MAAM,wBAAwB,MAAM;GACtC,OAAO,IAAI,aAAa,MAAM,iBAAiB;IAC7C,MAAM,wBAAwB;IAC9B,IAAI,MAAM,UAAU,wBAAwB,OAAO,KAAK,UAAU,kBAAkB,UAClF,MAAM,gBAAgB;GAE1B;GACA,IAAI;IACF,MAAM,KAAK,QAAQ,QAAQ,OAAO;KAChC,OAAO,KAAK;KACZ,IAAI,KAAK;KACT,QAAQ;KACR,kBAAkB,KAAK;KACvB,OAAO,EAAE,UAAU,MAAM;IAC3B,CAAC;IACD;GACF,SAAS,OAAO;IACd,IAAI,EAAE,iBAAiB,8BAA8B,MAAM;GAC7D;EACF;CACF;CAEA,MAAM,SAAS,eAAyB,WAAmB,UAAoC;EAC7F,KAAK,MAAM,gBAAgB,eACzB,IAAI;GACF,MAAM,QAAQ,MAAM,KAAK,QAAQ,OAAO,4BAA4B,cAAc,WAAW,QAAQ;GACrG,IAAI,UAAU,KAAA,GAAW,OAAO,SAAA;EAClC,QAAQ,CAER;EAEF,OAAO;CACT;CAEA,MAAM,UACJ,OA0BA,UAAU,GAC0B;EACpC,MAAM,OAAO,OAAO,MAAM,OAAO,QAAQ,iBAAiB;EAC1D,MAAM,cAAc,MAAM;EAC1B,MAAM,qBAAqB,MAAM;EACjC,MAAM,oBAAoB,OAAO,MAAM,cAAc,KAAK;EAC1D,MAAM,UAAoC;GACxC,QAAQ;IAAE,OAAO,MAAM;IAAO,WAAW,MAAM;GAAiB;GAChE,OAAO,MAAM;GACb,SAAS;IAAE,MAAM;IAAU,IAAI,MAAM;GAAgB;GACrD,OAAO,UAAU,MAAM;GACvB,aAAa,CAAC;GACd,eAAe,KAAK,QAAQ;GAC5B,GAAI,MAAM,OACN;IACE,MAAM;KACJ,IAAI,MAAM,KAAK;KACf,QACE,MAAM,KAAK,gBAAgB,SAAS,iBAC/B,cACD,MAAM,KAAK,gBAAgB,kBAAkB,WAC1C,iBACA;KACT,WAAW,MAAM,KAAK,gBAAgB,cAAc;KACpD,kBAAkB,MAAM,KAAK;KAC7B,OAAO,MAAM,KAAK;KAClB,KAAK,MAAM,KAAK,gBAAgB,OAAO;KACvC,QAAQ,MAAM,KAAK;KACnB,YAAY,MAAM,KAAK;KACvB,UAAU,MAAM,KAAK;IACvB;IACA,OAAO,iBAAiB,MAAM,IAAI;IAClC,cAAc,MAAM,KAAK;IACzB,GAAG,iBAAiB,KAAK,QAAQ,QAAQ,MAAM,IAAI;IACnD,GAAG,eAAe,KAAK,QAAQ,QAAQ,MAAM,IAAI;GACnD,IACA,CAAC;GACL,QAAQ,MAAM;GACd,OAAO,MAAM;GACb,YAAY,MAAM,OAAO;GACzB,SAAS,EAAE,WAAW,MAAM,eAAe,UAAU,YAAY,EAAE;GACnE,YAAY;IAAE,IAAI,MAAM;IAAW,UAAU,MAAM;IAAa,MAAM,MAAM;GAAK;GACjF,GAAI,MAAM,YAAY,MAAM,kBAAkB,OAAO,MAAM,OAAO,KAAK,IACnE,EACE,OAAO;IACL,QAAQ,MAAM;IACd,WAAW,MAAM;IACjB,OAAO,OAAO,MAAM,OAAO,KAAK;IAChC,KACE,OAAO,MAAM,OAAO,GAAG,KACvB,OAAO,MAAM,OAAO,OAAO,KAC3B,WAAW,MAAM,KAAK,GAAG,MAAM,YAAY,YAAY,MAAM;IAC/D,GAAI,OAAO,MAAM,OAAO,UAAU,IAAI,EAAE,WAAW,OAAO,MAAM,OAAO,UAAU,EAAE,IAAI,CAAC;IACxF,GAAI,OAAO,MAAM,OAAO,UAAU,IAAI,EAAE,WAAW,OAAO,MAAM,OAAO,UAAU,EAAE,IAAI,CAAC;IACxF,WAAW,UAAU,MAAM,OAAO,QAAQ,aAAa,MAAM,OAAO,SAAS;IAC7E,QAAQ,WAAW,MAAM,OAAO,UAAU,MAAM,OAAO,QAAQ,MAAM;IACrE,aAAa,YAAY,MAAM,OAAO,UAAU,MAAM,OAAO,QAAQ,MAAM;IAC3E,OAAO,OAAO,MAAM,OAAO,KAAK,MAAM,WAAY,WAAsB;IACxE,GAAI,cAAc,EAAE,QAAQ,YAAY,IAAI,CAAC;IAC7C,eAAe,MAAM;GACvB,EACF,IACA,CAAC;GACL,GAAI,MAAM,OAAO,UAAU,eAAe,OAAO,MAAM,EAAE,IACrD,EACE,MAAM;IACJ,IAAI,OAAO,MAAM,EAAE;IACnB,GAAI,OAAO,MAAM,IAAI,IAAI,EAAE,MAAM,OAAO,MAAM,IAAI,EAAE,IAAI,CAAC;IACzD,GAAI,OAAO,MAAM,GAAG,IAAI,EAAE,KAAK,OAAO,MAAM,GAAG,EAAE,IAAI,CAAC;IACtD,GAAI,OAAO,OAAO,MAAM,OAAO,QAAQ,IAAI,CAAC,EAAE,QAAQ,IAClD,EAAE,QAAQ,OAAO,OAAO,MAAM,OAAO,QAAQ,IAAI,CAAC,EAAE,QAAQ,EAAE,IAC9D,CAAC;IACL,GAAI,OAAO,MAAM,UAAU,IAAI,EAAE,WAAW,OAAO,MAAM,UAAU,EAAE,IAAI,CAAC;IAC1E,GAAI,OAAO,MAAM,UAAU,IAAI,EAAE,WAAW,OAAO,MAAM,UAAU,EAAE,IAAI,CAAC;GAC5E,EACF,IACA,CAAC;GACL,GAAI,MAAM,mBAAmB,MAAM,mBAAmB,OAAO,MAAM,cAAc,KAAK,IAClF,EACE,cAAc;IACZ,QAAQ,MAAM;IACd,WAAW,MAAM;IACjB,OAAO,OAAO,MAAM,cAAc,KAAK;IACvC,KACE,OAAO,MAAM,cAAc,GAAG,KAC9B,OAAO,MAAM,cAAc,OAAO,KAClC,WAAW,MAAM,KAAK,GAAG,MAAM,YAAY,oBAAoB,MAAM;IACvE,GAAI,OAAO,MAAM,cAAc,UAAU,IAAI,EAAE,WAAW,OAAO,MAAM,cAAc,UAAU,EAAE,IAAI,CAAC;IACtG,OACE,sBAAsB,YAAY,sBAAsB,WACnD,WACA;IACP,OAAO,QAAQ,MAAM,cAAc,KAAK,KAAK,QAAQ,MAAM,cAAc,gBAAgB,KAAK;IAC9F,QAAQ,MAAM,UAAU,wBAAwB,sBAAsB;IACtE,WAAW,UAAU,MAAM,OAAO,QAAQ,aAAa,MAAM,cAAc,SAAS;IACpF,WAAW,UAAU,MAAM,OAAO,QAAQ,aAAa,MAAM,cAAc,SAAS;IACpF,QAAQ,WAAW,MAAM,cAAc,UAAU,MAAM,OAAO,QAAQ,MAAM;IAC5E,aAAa,YAAY,MAAM,cAAc,UAAU,MAAM,OAAO,QAAQ,MAAM;IAClF,GAAI,qBAAqB,EAAE,QAAQ,mBAAmB,IAAI,CAAC;IAC3D,eAAe,MAAM;IACrB,iBAAiB,MAAM;IACvB,YAAY,OAAO,MAAM,cAAc,aAAa,KAAK;IACzD,YAAY,OAAO,MAAM,cAAc,aAAa,KAAK;GAC3D,EACF,IACA,CAAC;EACP;EACA,MAAM,aAAa,mBACjB,KAAK,QAAQ,QACb,MAAM,4BAA4B,WAAW,KAAA,IAAY,aAAa,OAAO,GAC7E,QACF;EACA,MAAM,OACJ,MAAM,4BAA4B,UAC9B,wBAAwB,KAAK,QAAQ,OAAO,OAAO,MAAM,KAAK,IAC5D,KAAA,IACA,YAAY,qBACd,gBAAgB,KAAK,QAAQ,OAAO,OAAO,YAAY,MAAM,KAAK;EAGxE,IAAI,MAAM,4BAA4B,WAAW,CAAC,MAAM,OAAO,EAAE,QAAQ,UAAU;EACnF,IAAI;EACJ,IAAI,YAAuC,CAAC;EAC5C,IAAI,UAA+E,EAAE,QAAQ,WAAW;EACxG,IAAI;GACF,WAAW,OAAO,MAAM,gBAAgB,QAAQ,QAAQ,KAAK,OAAO,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,KAAA;GACzF,IAAI,UAAU,SAAS,UACrB,UAAU;IAAE,QAAQ;IAAY,MAAM,SAAS;IAAM,QAAQ,SAAS;GAAO;QACxE,IAAI,UACT,YAAY,6BAA6B,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAI,UAAS;IAChE,4BACE,OACA,KAAK,QAAQ,QACb,MAAM,OAAO,iBAAiB,MAAM,IAAI,IAAI,KAAA,CAC9C;IACA,OAAO,EAAE,GAAG,MAAM;GACpB,CAAC;EAEL,SAAS,OAAO;GACd,MAAM,WAAW,iBAAiB,SAAS,MAAM,YAAY;GAC7D,UAAU;IACR,QAAQ;IACR,MAAM,WAAW,YAAY;IAC7B,QAAQ,WACJ,uCACA,iBAAiB,QACf,MAAM,QAAQ,MAAM,GAAG,GAAK,IAC5B;GACR;EACF;EAEA,MAAM,YAAY,MAAM,KAAK,QAAQ,QAAQ,qBAAqB;GAChE,OAAO,MAAM;GACb,kBAAkB,MAAM;GACxB,YAAY,MAAM,MAAM,MAAM;GAC9B,SAAS;IAAE,UAAU,MAAM;IAAiB,aAAa,UAAU,MAAM;GAAQ;GACjF,eAAe,KAAK,QAAQ;GAC5B,kBAAkB,MAAM,MAAM,YAAY;GAC1C,OAAO,EAAE,GAAG,MAAM,MAAM;GACxB;GACA;GACA,aAAa,CAAC;GACd,qBAAK,IAAI,KAAK;EAChB,CAAC;EACD,IAAI,UAAU,WAAW,SAAS;GAEhC,IAAI,WAAW,yBACb,MAAM,IAAI,MAAM,sCAAsC,MAAM,gBAAgB,6BAA6B;GAE3G,MAAM,QAAQ,MAAM,OAAO,MAAM,KAAK,QAAQ,QAAQ,IAAI;IAAE,OAAO,MAAM;IAAO,IAAI,MAAM,KAAK;GAAG,CAAC,IAAI;GACvG,IAAI,MAAM,QAAQ,CAAC,OAAO,OAAO,EAAE,QAAQ,UAAU;GACrD,OAAO,KAAK,UAAU;IAAE,GAAG;IAAO,MAAM,SAAS,KAAA;GAAU,GAAG,UAAU,CAAC;EAC3E;EACA,OAAO,EAAE,QAAQ,UAAU,OAAO;CACpC;AACF;AAEA,SAAgB,kBACd,QACA,SACgE;CAChE,IAAI,CAAC,QAAQ,SAAS,OAAO,KAAA;CAC7B,MAAM,QAAQ,IAAI,YAAY;EAC5B;EACA,eAAe,QAAQ,QAAQ;EAC/B,UAAU,QAAQ,QAAQ;EAC1B,SAAS,QAAQ,QAAQ;EACzB,QAAQ,QAAQ,QAAQ;EACxB,eAAe,QAAQ,QAAQ;EAC/B,QAAQ,QAAQ,QAAQ;CAC1B,CAAC;CACD,QAAO,UAAS,MAAM,OAAO,KAAK;AACpC"}
@@ -133,7 +133,7 @@ export interface JiraTransition {
133
133
  export declare class JiraApiClient {
134
134
  #private;
135
135
  constructor(config: JiraApiClientConfig);
136
- /** Normalized site base URL (no trailing slash) — used for `/browse/<key>` links. */
136
+ /** Normalized API base URL (no trailing slash). */
137
137
  get baseUrl(): string;
138
138
  getServerInfo(): Promise<JiraServerInfo>;
139
139
  /** One page of the site's projects (Settings intake-source picker). */
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/integrations/jira/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/integrations/jira/api.ts"],"names":[],"mappings":"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"}
@@ -66,7 +66,7 @@ var JiraApiClient = class {
66
66
  this.#baseUrl = config.baseUrl.replace(/\/+$/, "");
67
67
  this.#fetch = config.fetchImpl ?? globalThis.fetch;
68
68
  }
69
- /** Normalized site base URL (no trailing slash) — used for `/browse/<key>` links. */
69
+ /** Normalized API base URL (no trailing slash). */
70
70
  get baseUrl() {
71
71
  return this.#baseUrl;
72
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","names":[],"sources":["../../../src/integrations/jira/api.ts"],"sourcesContent":["/**\n * Typed Jira Cloud REST client (API v3).\n *\n * Shared by the direct and Platform-managed Jira integrations. Direct Jira\n * uses Basic auth against an Atlassian site; Platform Jira points the same\n * client at a connection proxy with a bearer token. Both modes use the same\n * request shapes, timeout, and error normalization.\n *\n * Endpoint notes (Jira Cloud, 2026):\n * - Issue search is `POST /rest/api/3/search/jql` — the legacy\n * `GET /rest/api/3/search` endpoint has been removed. The new endpoint\n * returns only ids unless `fields` is requested explicitly, and pages with\n * an opaque `nextPageToken` cursor (no `startAt`/`total`).\n * - Project listing is `GET /rest/api/3/project/search` with classic\n * `startAt`/`isLast` paging.\n * - Descriptions and comment bodies are ADF documents (see `adf.ts`).\n */\n\nimport type { AdfNode } from './adf.js';\nimport { textToAdf } from './adf.js';\n\nconst JIRA_TIMEOUT_MS = 15_000;\n/** Hosts allowed to receive Basic credentials over plain http (local development mocks). */\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\n/** Issue page size — Linear parity (`LINEAR_ISSUES_PAGE_SIZE`). */\nexport const JIRA_ISSUES_PAGE_SIZE = 30;\n/** Comment page size — Linear parity (`LINEAR_COMMENTS_PAGE_SIZE`). */\nexport const JIRA_COMMENTS_PAGE_SIZE = 50;\nexport const JIRA_PROJECTS_PAGE_SIZE = 50;\n\n/** Fields requested from issue reads — `search/jql` returns ids only without an explicit list. */\nexport const JIRA_ISSUE_FIELDS = [\n 'summary',\n 'description',\n 'status',\n 'assignee',\n 'reporter',\n 'labels',\n 'priority',\n 'issuetype',\n 'project',\n 'created',\n 'updated',\n] as const;\n\ninterface JiraApiClientBaseConfig {\n /** Jira API base URL, either the Atlassian site or a Platform connection proxy. */\n baseUrl: string;\n fetchImpl?: typeof fetch;\n}\n\nexport type JiraApiClientConfig = JiraApiClientBaseConfig &\n (\n | {\n /** Atlassian account email the API token belongs to. */\n email: string;\n /** API token from id.atlassian.com → Security → API tokens. */\n apiToken: string;\n accessToken?: never;\n }\n | {\n /** Bearer token used when the base URL is a Mastra Platform connection proxy. */\n accessToken: string;\n /**\n * Mastra project id sent as `x-mastra-project-id` so the platform can\n * attribute proxied Jira activity to the project. Falls back to the\n * `MASTRA_PROJECT_ID` environment variable. The platform strips the\n * header before forwarding the request to Atlassian.\n */\n projectId?: string;\n email?: never;\n apiToken?: never;\n }\n );\n\n/** Stable error code the routes/tools surface to the SPA and agents. */\nexport type JiraApiErrorCode = 'jira_auth_failed' | 'jira_request_failed';\n\n/** Normalized Jira API failure carrying the HTTP status and Jira's error detail. */\nexport class JiraApiError extends Error {\n readonly status: number | null;\n readonly code: JiraApiErrorCode;\n\n constructor(message: string, status: number | null) {\n super(message);\n this.name = 'JiraApiError';\n this.status = status;\n this.code = status === 401 || status === 403 ? 'jira_auth_failed' : 'jira_request_failed';\n }\n}\n\nexport interface JiraStatusCategory {\n /** `new` | `indeterminate` | `done` — Jira's fixed status families. */\n key: string;\n name?: string;\n}\n\nexport interface JiraStatus {\n name: string;\n statusCategory?: JiraStatusCategory;\n}\n\nexport interface JiraProject {\n id: string;\n key: string;\n name: string;\n}\n\nexport interface JiraProjectSearchPage {\n values: JiraProject[];\n startAt: number;\n isLast: boolean;\n}\n\nexport interface JiraServerInfo {\n baseUrl: string;\n serverTitle?: string;\n}\n\nexport interface JiraUser {\n displayName: string;\n}\n\nexport interface JiraComment {\n id: string;\n author?: JiraUser | null;\n body?: AdfNode | null;\n created: string;\n}\n\nexport interface JiraCommentPage {\n comments: JiraComment[];\n startAt: number;\n maxResults: number;\n total: number;\n}\n\nexport interface JiraIssueFields {\n summary?: string;\n description?: AdfNode | null;\n status?: JiraStatus;\n assignee?: JiraUser | null;\n reporter?: JiraUser | null;\n labels?: string[];\n priority?: { name: string } | null;\n issuetype?: { name: string };\n project?: { id: string; key: string; name?: string };\n created?: string;\n updated?: string;\n}\n\nexport interface JiraIssue {\n id: string;\n key: string;\n fields: JiraIssueFields;\n}\n\nexport interface JiraSearchPage {\n issues: JiraIssue[];\n /** Opaque cursor for the next page; absent on the last page. */\n nextPageToken?: string | null;\n}\n\nexport interface JiraTransition {\n id: string;\n name: string;\n /** Target status the transition moves the issue to. */\n to?: JiraStatus;\n}\n\nexport class JiraApiClient {\n readonly #baseUrl: string;\n readonly #authHeader: string;\n readonly #projectId: string | null;\n readonly #fetch: typeof fetch;\n\n constructor(config: JiraApiClientConfig) {\n if (!config.baseUrl) {\n throw new Error('JiraApiClient is missing required config: baseUrl.');\n }\n // Fail fast on a malformed base URL: a bare host like `acme.atlassian.net`\n // would otherwise surface later as an opaque `TypeError: Invalid URL`.\n let parsed: URL;\n try {\n parsed = new URL(config.baseUrl);\n } catch {\n throw new Error(`JiraApiClient baseUrl is not an absolute URL: \"${config.baseUrl}\".`);\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`JiraApiClient baseUrl must be an http(s) URL: \"${config.baseUrl}\".`);\n }\n if (config.accessToken !== undefined) {\n if (!config.accessToken) {\n throw new Error('JiraApiClient is missing required config: accessToken.');\n }\n this.#authHeader = `Bearer ${config.accessToken}`;\n this.#projectId = (config.projectId ?? process.env.MASTRA_PROJECT_ID)?.trim() || null;\n } else {\n const missing = (['email', 'apiToken'] as const).filter(key => !config[key]);\n if (missing.length > 0) {\n throw new Error(`JiraApiClient is missing required config: ${missing.join(', ')}.`);\n }\n // Basic credentials ride on every request, so never send them over\n // plaintext to a remote host. Loopback stays allowed for local mocks.\n if (parsed.protocol === 'http:' && !LOOPBACK_HOSTS.has(parsed.hostname)) {\n throw new Error('JiraApiClient baseUrl must use https when Basic credentials are configured.');\n }\n this.#authHeader = `Basic ${Buffer.from(`${config.email}:${config.apiToken}`).toString('base64')}`;\n // Direct Atlassian mode: never send Mastra platform headers to Jira itself.\n this.#projectId = null;\n }\n this.#baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this.#fetch = config.fetchImpl ?? globalThis.fetch;\n }\n\n /** Normalized site base URL (no trailing slash) — used for `/browse/<key>` links. */\n get baseUrl(): string {\n return this.#baseUrl;\n }\n\n async #request<T>(\n method: 'GET' | 'POST',\n path: string,\n options: { query?: Record<string, string | number | undefined>; body?: unknown } = {},\n ): Promise<T> {\n const url = new URL(`${this.#baseUrl}${path}`);\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n const hasBody = options.body !== undefined;\n const res = await this.#fetch(url, {\n method,\n signal: AbortSignal.timeout(JIRA_TIMEOUT_MS),\n headers: {\n authorization: this.#authHeader,\n accept: 'application/json',\n // Project attribution for the Mastra platform proxy; stripped by the\n // platform before the request reaches Atlassian.\n ...(this.#projectId ? { 'x-mastra-project-id': this.#projectId } : {}),\n ...(hasBody ? { 'content-type': 'application/json' } : {}),\n },\n ...(hasBody ? { body: JSON.stringify(options.body) } : {}),\n });\n if (!res.ok) {\n // Jira reports validation failures as `errorMessages` and `errors`;\n // the Platform proxy reports upstream failures through `detail`/`error`/`title`.\n let detail: string | null = null;\n try {\n const errBody = (await res.json()) as {\n errorMessages?: string[];\n errors?: Record<string, string>;\n detail?: string;\n error?: string;\n title?: string;\n };\n detail =\n errBody.errorMessages?.[0] ??\n Object.values(errBody.errors ?? {})[0] ??\n errBody.detail ??\n errBody.error ??\n errBody.title ??\n null;\n } catch {\n // Non-JSON error body; fall back to the status code alone.\n }\n throw new JiraApiError(`Jira API request failed (${res.status})${detail ? `: ${detail}` : ''}`, res.status);\n }\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n\n async getServerInfo(): Promise<JiraServerInfo> {\n return this.#request<JiraServerInfo>('GET', '/rest/api/3/serverInfo');\n }\n\n /** One page of the site's projects (Settings intake-source picker). */\n async listProjects(options: { startAt?: number } = {}): Promise<JiraProjectSearchPage> {\n return this.#request<JiraProjectSearchPage>('GET', '/rest/api/3/project/search', {\n query: { startAt: options.startAt ?? 0, maxResults: JIRA_PROJECTS_PAGE_SIZE },\n });\n }\n\n /** One page of a JQL search with explicitly requested fields. */\n async searchIssues(options: {\n jql: string;\n fields?: readonly string[];\n nextPageToken?: string;\n maxResults?: number;\n }): Promise<JiraSearchPage> {\n return this.#request<JiraSearchPage>('POST', '/rest/api/3/search/jql', {\n body: {\n jql: options.jql,\n fields: [...(options.fields ?? JIRA_ISSUE_FIELDS)],\n maxResults: options.maxResults ?? JIRA_ISSUES_PAGE_SIZE,\n ...(options.nextPageToken ? { nextPageToken: options.nextPageToken } : {}),\n },\n });\n }\n\n /** Full issue detail by key (`ENG-42`) or id. */\n async getIssue(keyOrId: string): Promise<JiraIssue> {\n return this.#request<JiraIssue>('GET', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}`, {\n query: { fields: JIRA_ISSUE_FIELDS.join(',') },\n });\n }\n\n /** One page of an issue's comments (oldest first, Jira default order). */\n async listComments(keyOrId: string, options: { startAt?: number } = {}): Promise<JiraCommentPage> {\n return this.#request<JiraCommentPage>('GET', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/comment`, {\n query: { startAt: options.startAt ?? 0, maxResults: JIRA_COMMENTS_PAGE_SIZE },\n });\n }\n\n /** Post a plain-text comment (wrapped into a minimal ADF document). */\n async createComment(keyOrId: string, body: string): Promise<JiraComment> {\n return this.#request<JiraComment>('POST', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/comment`, {\n body: { body: textToAdf(body) },\n });\n }\n\n /** Transitions currently legal for the issue (workflow-dependent). */\n async listTransitions(keyOrId: string): Promise<JiraTransition[]> {\n const data = await this.#request<{ transitions?: JiraTransition[] }>(\n 'GET',\n `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/transitions`,\n );\n return data.transitions ?? [];\n }\n\n /** Apply a transition by id (Jira responds 204 on success). */\n async applyTransition(keyOrId: string, transitionId: string): Promise<void> {\n await this.#request<void>('POST', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/transitions`, {\n body: { transition: { id: transitionId } },\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"api.js","names":[],"sources":["../../../src/integrations/jira/api.ts"],"sourcesContent":["/**\n * Typed Jira Cloud REST client (API v3).\n *\n * Shared by the direct and Platform-managed Jira integrations. Direct Jira\n * uses Basic auth against an Atlassian site; Platform Jira points the same\n * client at a connection proxy with a bearer token. Both modes use the same\n * request shapes, timeout, and error normalization.\n *\n * Endpoint notes (Jira Cloud, 2026):\n * - Issue search is `POST /rest/api/3/search/jql` — the legacy\n * `GET /rest/api/3/search` endpoint has been removed. The new endpoint\n * returns only ids unless `fields` is requested explicitly, and pages with\n * an opaque `nextPageToken` cursor (no `startAt`/`total`).\n * - Project listing is `GET /rest/api/3/project/search` with classic\n * `startAt`/`isLast` paging.\n * - Descriptions and comment bodies are ADF documents (see `adf.ts`).\n */\n\nimport type { AdfNode } from './adf.js';\nimport { textToAdf } from './adf.js';\n\nconst JIRA_TIMEOUT_MS = 15_000;\n/** Hosts allowed to receive Basic credentials over plain http (local development mocks). */\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\n/** Issue page size — Linear parity (`LINEAR_ISSUES_PAGE_SIZE`). */\nexport const JIRA_ISSUES_PAGE_SIZE = 30;\n/** Comment page size — Linear parity (`LINEAR_COMMENTS_PAGE_SIZE`). */\nexport const JIRA_COMMENTS_PAGE_SIZE = 50;\nexport const JIRA_PROJECTS_PAGE_SIZE = 50;\n\n/** Fields requested from issue reads — `search/jql` returns ids only without an explicit list. */\nexport const JIRA_ISSUE_FIELDS = [\n 'summary',\n 'description',\n 'status',\n 'assignee',\n 'reporter',\n 'labels',\n 'priority',\n 'issuetype',\n 'project',\n 'created',\n 'updated',\n] as const;\n\ninterface JiraApiClientBaseConfig {\n /** Jira API base URL, either the Atlassian site or a Platform connection proxy. */\n baseUrl: string;\n fetchImpl?: typeof fetch;\n}\n\nexport type JiraApiClientConfig = JiraApiClientBaseConfig &\n (\n | {\n /** Atlassian account email the API token belongs to. */\n email: string;\n /** API token from id.atlassian.com → Security → API tokens. */\n apiToken: string;\n accessToken?: never;\n }\n | {\n /** Bearer token used when the base URL is a Mastra Platform connection proxy. */\n accessToken: string;\n /**\n * Mastra project id sent as `x-mastra-project-id` so the platform can\n * attribute proxied Jira activity to the project. Falls back to the\n * `MASTRA_PROJECT_ID` environment variable. The platform strips the\n * header before forwarding the request to Atlassian.\n */\n projectId?: string;\n email?: never;\n apiToken?: never;\n }\n );\n\n/** Stable error code the routes/tools surface to the SPA and agents. */\nexport type JiraApiErrorCode = 'jira_auth_failed' | 'jira_request_failed';\n\n/** Normalized Jira API failure carrying the HTTP status and Jira's error detail. */\nexport class JiraApiError extends Error {\n readonly status: number | null;\n readonly code: JiraApiErrorCode;\n\n constructor(message: string, status: number | null) {\n super(message);\n this.name = 'JiraApiError';\n this.status = status;\n this.code = status === 401 || status === 403 ? 'jira_auth_failed' : 'jira_request_failed';\n }\n}\n\nexport interface JiraStatusCategory {\n /** `new` | `indeterminate` | `done` — Jira's fixed status families. */\n key: string;\n name?: string;\n}\n\nexport interface JiraStatus {\n name: string;\n statusCategory?: JiraStatusCategory;\n}\n\nexport interface JiraProject {\n id: string;\n key: string;\n name: string;\n}\n\nexport interface JiraProjectSearchPage {\n values: JiraProject[];\n startAt: number;\n isLast: boolean;\n}\n\nexport interface JiraServerInfo {\n baseUrl: string;\n serverTitle?: string;\n}\n\nexport interface JiraUser {\n displayName: string;\n}\n\nexport interface JiraComment {\n id: string;\n author?: JiraUser | null;\n body?: AdfNode | null;\n created: string;\n}\n\nexport interface JiraCommentPage {\n comments: JiraComment[];\n startAt: number;\n maxResults: number;\n total: number;\n}\n\nexport interface JiraIssueFields {\n summary?: string;\n description?: AdfNode | null;\n status?: JiraStatus;\n assignee?: JiraUser | null;\n reporter?: JiraUser | null;\n labels?: string[];\n priority?: { name: string } | null;\n issuetype?: { name: string };\n project?: { id: string; key: string; name?: string };\n created?: string;\n updated?: string;\n}\n\nexport interface JiraIssue {\n id: string;\n key: string;\n fields: JiraIssueFields;\n}\n\nexport interface JiraSearchPage {\n issues: JiraIssue[];\n /** Opaque cursor for the next page; absent on the last page. */\n nextPageToken?: string | null;\n}\n\nexport interface JiraTransition {\n id: string;\n name: string;\n /** Target status the transition moves the issue to. */\n to?: JiraStatus;\n}\n\nexport class JiraApiClient {\n readonly #baseUrl: string;\n readonly #authHeader: string;\n readonly #projectId: string | null;\n readonly #fetch: typeof fetch;\n\n constructor(config: JiraApiClientConfig) {\n if (!config.baseUrl) {\n throw new Error('JiraApiClient is missing required config: baseUrl.');\n }\n // Fail fast on a malformed base URL: a bare host like `acme.atlassian.net`\n // would otherwise surface later as an opaque `TypeError: Invalid URL`.\n let parsed: URL;\n try {\n parsed = new URL(config.baseUrl);\n } catch {\n throw new Error(`JiraApiClient baseUrl is not an absolute URL: \"${config.baseUrl}\".`);\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`JiraApiClient baseUrl must be an http(s) URL: \"${config.baseUrl}\".`);\n }\n if (config.accessToken !== undefined) {\n if (!config.accessToken) {\n throw new Error('JiraApiClient is missing required config: accessToken.');\n }\n this.#authHeader = `Bearer ${config.accessToken}`;\n this.#projectId = (config.projectId ?? process.env.MASTRA_PROJECT_ID)?.trim() || null;\n } else {\n const missing = (['email', 'apiToken'] as const).filter(key => !config[key]);\n if (missing.length > 0) {\n throw new Error(`JiraApiClient is missing required config: ${missing.join(', ')}.`);\n }\n // Basic credentials ride on every request, so never send them over\n // plaintext to a remote host. Loopback stays allowed for local mocks.\n if (parsed.protocol === 'http:' && !LOOPBACK_HOSTS.has(parsed.hostname)) {\n throw new Error('JiraApiClient baseUrl must use https when Basic credentials are configured.');\n }\n this.#authHeader = `Basic ${Buffer.from(`${config.email}:${config.apiToken}`).toString('base64')}`;\n // Direct Atlassian mode: never send Mastra platform headers to Jira itself.\n this.#projectId = null;\n }\n this.#baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this.#fetch = config.fetchImpl ?? globalThis.fetch;\n }\n\n /** Normalized API base URL (no trailing slash). */\n get baseUrl(): string {\n return this.#baseUrl;\n }\n\n async #request<T>(\n method: 'GET' | 'POST',\n path: string,\n options: { query?: Record<string, string | number | undefined>; body?: unknown } = {},\n ): Promise<T> {\n const url = new URL(`${this.#baseUrl}${path}`);\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n const hasBody = options.body !== undefined;\n const res = await this.#fetch(url, {\n method,\n signal: AbortSignal.timeout(JIRA_TIMEOUT_MS),\n headers: {\n authorization: this.#authHeader,\n accept: 'application/json',\n // Project attribution for the Mastra platform proxy; stripped by the\n // platform before the request reaches Atlassian.\n ...(this.#projectId ? { 'x-mastra-project-id': this.#projectId } : {}),\n ...(hasBody ? { 'content-type': 'application/json' } : {}),\n },\n ...(hasBody ? { body: JSON.stringify(options.body) } : {}),\n });\n if (!res.ok) {\n // Jira reports validation failures as `errorMessages` and `errors`;\n // the Platform proxy reports upstream failures through `detail`/`error`/`title`.\n let detail: string | null = null;\n try {\n const errBody = (await res.json()) as {\n errorMessages?: string[];\n errors?: Record<string, string>;\n detail?: string;\n error?: string;\n title?: string;\n };\n detail =\n errBody.errorMessages?.[0] ??\n Object.values(errBody.errors ?? {})[0] ??\n errBody.detail ??\n errBody.error ??\n errBody.title ??\n null;\n } catch {\n // Non-JSON error body; fall back to the status code alone.\n }\n throw new JiraApiError(`Jira API request failed (${res.status})${detail ? `: ${detail}` : ''}`, res.status);\n }\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n\n async getServerInfo(): Promise<JiraServerInfo> {\n return this.#request<JiraServerInfo>('GET', '/rest/api/3/serverInfo');\n }\n\n /** One page of the site's projects (Settings intake-source picker). */\n async listProjects(options: { startAt?: number } = {}): Promise<JiraProjectSearchPage> {\n return this.#request<JiraProjectSearchPage>('GET', '/rest/api/3/project/search', {\n query: { startAt: options.startAt ?? 0, maxResults: JIRA_PROJECTS_PAGE_SIZE },\n });\n }\n\n /** One page of a JQL search with explicitly requested fields. */\n async searchIssues(options: {\n jql: string;\n fields?: readonly string[];\n nextPageToken?: string;\n maxResults?: number;\n }): Promise<JiraSearchPage> {\n return this.#request<JiraSearchPage>('POST', '/rest/api/3/search/jql', {\n body: {\n jql: options.jql,\n fields: [...(options.fields ?? JIRA_ISSUE_FIELDS)],\n maxResults: options.maxResults ?? JIRA_ISSUES_PAGE_SIZE,\n ...(options.nextPageToken ? { nextPageToken: options.nextPageToken } : {}),\n },\n });\n }\n\n /** Full issue detail by key (`ENG-42`) or id. */\n async getIssue(keyOrId: string): Promise<JiraIssue> {\n return this.#request<JiraIssue>('GET', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}`, {\n query: { fields: JIRA_ISSUE_FIELDS.join(',') },\n });\n }\n\n /** One page of an issue's comments (oldest first, Jira default order). */\n async listComments(keyOrId: string, options: { startAt?: number } = {}): Promise<JiraCommentPage> {\n return this.#request<JiraCommentPage>('GET', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/comment`, {\n query: { startAt: options.startAt ?? 0, maxResults: JIRA_COMMENTS_PAGE_SIZE },\n });\n }\n\n /** Post a plain-text comment (wrapped into a minimal ADF document). */\n async createComment(keyOrId: string, body: string): Promise<JiraComment> {\n return this.#request<JiraComment>('POST', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/comment`, {\n body: { body: textToAdf(body) },\n });\n }\n\n /** Transitions currently legal for the issue (workflow-dependent). */\n async listTransitions(keyOrId: string): Promise<JiraTransition[]> {\n const data = await this.#request<{ transitions?: JiraTransition[] }>(\n 'GET',\n `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/transitions`,\n );\n return data.transitions ?? [];\n }\n\n /** Apply a transition by id (Jira responds 204 on success). */\n async applyTransition(keyOrId: string, transitionId: string): Promise<void> {\n await this.#request<void>('POST', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/transitions`, {\n body: { transition: { id: transitionId } },\n });\n }\n}\n"],"mappings":"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"}