Files
kandev/apps/web/components/task/task-github-pr-url.ts
T
2026-06-23 17:28:52 +01:00

89 lines
2.6 KiB
TypeScript

import type { Repository } from "@/lib/types/http";
export type TaskPullRequestLinkTarget = {
id: string;
title: string;
repositoryId?: string;
repositories?: Array<{ repository_id: string }>;
};
export type GitHubPRRepoRef = {
owner: string;
repo: string;
repositoryId: string;
};
export type ParsedPullRequestURL = {
owner: string;
repo: string;
number: number;
url: string;
};
export function githubReposForTask(
task: TaskPullRequestLinkTarget,
repositories: Repository[],
): GitHubPRRepoRef[] {
const repoIds = new Set((task.repositories ?? []).map((repo) => repo.repository_id));
if (task.repositoryId) repoIds.add(task.repositoryId);
return repositories
.filter((repo) => repoIds.has(repo.id) && repo.provider === "github")
.map((repo) => ({
owner: repo.provider_owner,
repo: repo.provider_name,
repositoryId: repo.id,
}))
.filter((repo) => repo.owner && repo.repo);
}
export function parseGitHubPullRequestURL(input: string): ParsedPullRequestURL | null {
const match = input
.trim()
.match(/^(?:https?:\/\/)?github\.com\/([^/\s]+)\/([^/\s]+)\/pull\/(\d+)(?:[/?#].*)?$/i);
if (!match) return null;
const [, owner, repo, number] = match;
const parsedNumber = Number(number);
if (!Number.isSafeInteger(parsedNumber) || parsedNumber <= 0) return null;
return {
owner,
repo,
number: parsedNumber,
url: `https://github.com/${owner}/${repo}/pull/${parsedNumber}`,
};
}
function positivePullRequestNumber(input: string): string | null {
const number = input.replace(/^#/, "");
if (!/^\d+$/.test(number)) return null;
const parsedNumber = Number(number);
if (!Number.isSafeInteger(parsedNumber) || parsedNumber <= 0) return null;
return String(parsedNumber);
}
export function pullRequestPayload(input: string, githubRepos: GitHubPRRepoRef[]) {
const trimmed = input.trim();
const inferredNumber = positivePullRequestNumber(trimmed);
const inferredRepo = inferredNumber && githubRepos.length === 1 ? githubRepos[0] : null;
if (inferredRepo && inferredNumber) {
return {
pr_url: `https://github.com/${inferredRepo.owner}/${inferredRepo.repo}/pull/${inferredNumber}`,
repository_id: inferredRepo.repositoryId,
};
}
const parsed = parseGitHubPullRequestURL(trimmed);
const matchingRepo = parsed
? githubRepos.find(
(repo) =>
repo.owner.toLowerCase() === parsed.owner.toLowerCase() &&
repo.repo.toLowerCase() === parsed.repo.toLowerCase(),
)
: null;
return {
pr_url: parsed?.url ?? trimmed,
repository_id: matchingRepo?.repositoryId,
};
}