This repository has no description
1// Builders for the Tangled AT Protocol records we create.
2//
3// Shapes verified against tangled.org/core lexicons + generated Go types
4// (see docs/lexicon-spec.md). Notable realities:
5// • pull patch is a GZIPPED BLOB in rounds[].patchBlob — there is no `patch` string.
6// • comments are `sh.tangled.feed.comment` with a `markup.markdown` body object;
7// images ride in body.blobs[] and are referenced from body.text.
8// • repos are keyed by DID (v1.14): target.repo / issue.repo = target repo DID,
9// while createRecord.repo = the AUTHORING account DID.
10
11export const NSID = {
12 pull: "sh.tangled.repo.pull",
13 issue: "sh.tangled.repo.issue",
14 comment: "sh.tangled.feed.comment",
15 markdown: "sh.tangled.markup.markdown",
16} as const;
17
18/** atproto blob ref object as returned by uploadBlob; embedded verbatim in records. */
19export interface BlobRef {
20 $type: "blob";
21 ref: { $link: string };
22 mimeType: string;
23 size: number;
24}
25
26/** com.atproto.repo.strongRef — uri + cid of a record. */
27export interface StrongRef {
28 uri: string;
29 cid: string;
30}
31
32// --- pull -------------------------------------------------------------------
33
34export interface BuildPullArgs {
35 targetRepoDid: string; // target repository DID
36 targetBranch: string;
37 title: string;
38 body: string;
39 patchBlob: BlobRef; // gzipped git format-patch, already uploaded
40 createdAt: string;
41 sourceBranch?: string; // optional; omit for patch-only PRs
42 sourceRepoDid?: string;
43}
44
45export function buildPullRecord(a: BuildPullArgs) {
46 const record: Record<string, unknown> = {
47 $type: NSID.pull,
48 title: a.title,
49 body: a.body,
50 target: { repo: a.targetRepoDid, branch: a.targetBranch },
51 rounds: [{ patchBlob: a.patchBlob, createdAt: a.createdAt }],
52 createdAt: a.createdAt,
53 };
54 if (a.sourceBranch) {
55 record.source = a.sourceRepoDid
56 ? { branch: a.sourceBranch, repo: a.sourceRepoDid }
57 : { branch: a.sourceBranch };
58 }
59 return record;
60}
61
62// --- issue ------------------------------------------------------------------
63
64export interface BuildIssueArgs {
65 targetRepoDid: string;
66 title: string;
67 body: string;
68 createdAt: string;
69}
70
71export function buildIssueRecord(a: BuildIssueArgs) {
72 return {
73 $type: NSID.issue,
74 repo: a.targetRepoDid,
75 title: a.title,
76 body: a.body,
77 createdAt: a.createdAt,
78 };
79}
80
81// --- comment (feed.comment with markdown body) ------------------------------
82
83export interface MarkdownBody {
84 $type: typeof NSID.markdown;
85 text: string;
86 original: string;
87 blobs?: BlobRef[];
88}
89
90export function buildMarkdown(text: string, blobs?: BlobRef[]): MarkdownBody {
91 const body: MarkdownBody = { $type: NSID.markdown, text, original: text };
92 if (blobs && blobs.length) body.blobs = blobs;
93 return body;
94}
95
96export interface BuildCommentArgs {
97 subject: StrongRef; // the issue/pull record being commented on
98 body: MarkdownBody;
99 createdAt: string;
100 replyTo?: StrongRef;
101 /** Required when subject is a pull — which round the comment is on (0-based). */
102 pullRoundIdx?: number;
103}
104
105export function buildCommentRecord(a: BuildCommentArgs) {
106 const record: Record<string, unknown> = {
107 $type: NSID.comment,
108 subject: a.subject,
109 body: a.body,
110 createdAt: a.createdAt,
111 };
112 if (a.replyTo) record.replyTo = a.replyTo;
113 if (a.pullRoundIdx !== undefined) record.pullRoundIdx = a.pullRoundIdx;
114 return record;
115}
116
117// --- helpers ----------------------------------------------------------------
118
119/** Standard markdown image embedding an uploaded blob by its CDN url. */
120export function imageMarkdown(blobUrl: string, alt: string): string {
121 return ``;
122}
123
124/** com.atproto.sync.getBlob CDN url for a blob (used to embed in markdown text). */
125export function blobCdnUrl(service: string, did: string, cid: string): string {
126 return `${service.replace(/\/$/, "")}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(
127 did,
128 )}&cid=${encodeURIComponent(cid)}`;
129}