import { all, first, placeholders, run } from "./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" };
export async function linkIssue(pullIssueId: string, issueId: string, actorId: string): Promise<void> {
const count = await first<{ count: number }>(
"SELECT COUNT(*) AS count FROM pull_issue_links WHERE pull_issue_id = ?",
pullIssueId,
);
assertBelowCap(count?.count ?? 0, MAX_LINKED_ISSUES, "linked issues per pull request");
await 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`,
pullIssueId,
issueId,
actorId,
Date.now(),
);
}
export async function unlinkIssue(pullIssueId: string, issueId: string): Promise<void> {
await run("DELETE FROM pull_issue_links WHERE pull_issue_id = ? AND issue_id = ?", pullIssueId, issueId);
}
function keywordNumbers(body: string): number[] {
return [...new Set([...body.matchAll(CLOSING_KEYWORDS)].map((match) => Number(match[1])))].slice(
0,
MAX_KEYWORD_LINKS,
);
}