1import type {
2 ExecutionEnvironmentCapabilities,
3 ProjectId,
4 ThreadId,
5 ThreadPullRequestKey,
6} from "@t3tools/contracts";
7import { normalizeThreadPullRequestKey } from "@t3tools/shared/threadPullRequests";
8
9type LinkingCapabilities = Pick<
10 ExecutionEnvironmentCapabilities,
11 "threadPullRequests" | "threadPullRequestLinking"
12>;
13
14/** Capability negotiation keeps both single-link and multi-link environments usable. */
15export function threadPullRequestLinkMode(capabilities: LinkingCapabilities | null | undefined) {
16 return capabilities?.threadPullRequests === true
17 ? "multiple"
18 : capabilities?.threadPullRequestLinking === true
19 ? "single"
20 : "unsupported";
21}
22
23export function planThreadPullRequestMutation({
24 capabilities,
25 threadId,
26 reference,
27 legacyProjectId,
28 legacyRepository,
29 linked,
30}: {
31 capabilities: LinkingCapabilities | null | undefined;
32 threadId: ThreadId;
33 reference: ThreadPullRequestKey & { readonly url: string };
34 /** Older servers need the exact repository checkout; same-host routing is not available. */
35 legacyProjectId: ProjectId | null;
36 /** Provider selector for legacy APIs, which omit Azure organization and project paths. */
37 legacyRepository?: string | undefined;
38 linked: boolean;
39}) {
40 const key = normalizeThreadPullRequestKey(reference);
41 switch (threadPullRequestLinkMode(capabilities)) {
42 case "multiple":
43 return linked
44 ? {
45 type: "thread.pull-request.link" as const,
46 input: { threadId, ...reference, ...key, source: "manual" as const },
47 }
48 : {
49 type: "thread.pull-request.unlink" as const,
50 input: {
51 threadId,
52 ...key,
53 },
54 };
55 case "single":
56 if (linked && legacyProjectId === null) return null;
57 return {
58 type: "thread.meta.update" as const,
59 input: {
60 threadId,
61 linkedPullRequest:
62 linked && legacyProjectId !== null
63 ? {
64 projectId: legacyProjectId,
65 repository: legacyRepository ?? reference.repository,
66 number: reference.number,
67 url: reference.url,
68 }
69 : null,
70 },
71 };
72 case "unsupported":
73 return null;
74 }
75}