https://domlink.deployments.hotsocket.fyi/
1import * as x from "@/extras.ts";
2import { AtURI, DID, GetRecord, ListRecords } from "./atproto.ts";
3import { getUriRecord, MiniDoc } from "./slingshot.ts";
4import * as Constellation from "./constellation.ts";
5import { DocProxy } from "./caching.ts";
6
7export type Issue = {
8 body: string;
9 createdAt: string;
10 issueId: number;
11 owner: DID;
12 ownerHandle: string;
13 repo: string;
14 title: string;
15 uri: AtURI; // not part of actual issue
16};
17export enum IssueStateValues {
18 Open = "sh.tangled.repo.issue.state.open",
19 Closed = "sh.tangled.repo.issue.state.closed"
20}
21export type IssueState = {
22 issue: AtURI;
23 state: IssueStateValues;
24};
25
26export type IssueMeta = {
27 issue: Issue;
28 state: IssueState;
29};
30export type RepoRef = {
31 readonly owner: string;
32 readonly name: string;
33};
34type RepoCommon = {
35 readonly description: string;
36 readonly name: string;
37 readonly knot: string;
38 readonly spindle: string;
39 readonly createdAt: string;
40}
41type RawRepo = RepoCommon & {
42 readonly owner: string;
43};
44export type Repo = RepoCommon & {
45 readonly owner: MiniDoc; // The record itself returns a DID, but a MiniDoc is awfully handy.
46 readonly uri: AtURI;
47};
48
49export async function GetRepo(ref: RepoRef): Promise<Repo> {
50 const repoOwnerDoc = await DocProxy.get(ref.owner);
51 const ownedRepos = await ListRecords<RawRepo>(repoOwnerDoc.did, "sh.tangled.repo", repoOwnerDoc.pds);
52 const repoRecord = ownedRepos.records.find(x=>x.value.name == ref.name)!;
53 return {
54 ...repoRecord.value,
55 owner: repoOwnerDoc,
56 uri: AtURI.fromString(repoRecord.uri)
57 };
58}
59export async function GetIssues(repo: Repo, timeout: number = 10000) {
60 const allIssueRefs = await x.timeout(timeout, Constellation.getLinks(repo.uri.toString()!, "sh.tangled.repo.issue", ".repo"));
61 const issues = await Promise.all(allIssueRefs.map(async (uri)=>{
62 try {
63 const issue = (await x.timeout(timeout/5,getUriRecord<Issue>(uri))).value;
64 issue.uri = uri;
65 issue.ownerHandle = (await DocProxy.get(issue.owner)).handle;
66 return issue;
67 } catch {
68 console.log(`issue timed out: ${uri.toString()}`);
69 return null;
70 }
71 })) as unknown as Issue[]; // despite filtering them out, typescript still thinks there could be nulls
72 return issues.filter(x=>x!=null).sort((a,b)=>b.issueId-a.issueId);
73}
74export async function GetIssueState(issue: Issue) {
75 const ref = await Constellation.getLinks(issue.uri.toString()!, "sh.tangled.repo.issue.state", ".issue");
76 const state = GetRecord<IssueState>(ref[0]);
77 return state;
78}
79export async function GetIssueWithMeta(uri: AtURI) {
80 const issue = (await getUriRecord<Issue>(uri)).value;
81 const state = await GetIssueState(issue);
82 return {issue, state};
83}
84
85/** Takes a string like `tangled.sh/core` or `https://tangled.sh/@tangled.sh/core` and returns a {@link RepoRef} */
86export function StringRepoRef(repo: string): RepoRef {
87 // pick out repo info
88 const repoSplat = repo.split("//");
89 let repoOwner: string;
90 let repoName: string;
91 if (repoSplat.length == 1) {
92 const repoHalves = repo.split("/");
93 if (repoHalves.length == 1) throw new Error("invalid repo string");
94 repoOwner = repoHalves[0];
95 repoName = repoHalves[1];
96 } else {
97 if (repoSplat[0] == "https:") {
98 const repoUrlSplat = new URL(repo).pathname.split("/");
99 // [ "", "@tangled.sh", "core" ]
100 if (repoUrlSplat.length < 2) throw new Error("invalid repo url");
101 repoOwner = repoUrlSplat[1];
102 repoName = repoUrlSplat[2];
103 } else {
104 throw new Error("unknown repo uri scheme");
105 }
106 }
107 if (repoOwner.startsWith("@")) repoOwner = repoOwner.substring(1);
108 return {owner: repoOwner, name: repoName};
109}