import { placeholders } from "./db";
import { repoDb } from "./scoped-db";
import { loadIssue, setIssueState } from "./issues";
import type { PullRow } from "./pulls";
import { emitWebhook, publicUrl } from "./events/emit";
import { assertBelowCap } from "./limits";
import type { RepoRow } from "~/lib/types";
const CLOSING_KEYWORDS = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)\b/gi;
const MAX_KEYWORD_LINKS = 10;
export const MAX_LINKED_ISSUES = 20;
export type LinkedIssue = { number: number; title: string; state: "open" | "closed"; source: "manual" | "keyword" };
type LinkPull = Pick<PullRow, "repo_id" | "issue_id">;
export async function linkIssue(pull: LinkPull, issueId: string, actorId: string): Promise<void> {
const db = repoDb(pull.repo_id);
const count = await db.first<{ count: number }>(
"SELECT COUNT(*) AS count FROM pull_issue_links WHERE pull_issue_id = ?",
pull.issue_id,
);
assertBelowCap(count?.count ?? 0, MAX_LINKED_ISSUES, "linked issues per pull request");
await db.run(
`INSERT INTO pull_issue_links (pull_issue_id, issue_id, source, created_by, created_at) VALUES (?, ?, 'manual', ?, ?)
ON CONFLICT (pull_issue_id, issue_id) DO UPDATE SET source = 'manual', created_by = excluded.created_by`,
pull.issue_id,
issueId,
actorId,
Date.now(),
);
}
export async function unlinkIssue(pull: LinkPull, issueId: string): Promise<void> {
await repoDb(pull.repo_id).run(
"DELETE FROM pull_issue_links WHERE pull_issue_id = ? AND issue_id = ?",
pull.issue_id,
issueId,
);
}