Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150import { ClientResponseError, ok } from "@atcute/client";import type { Cid, Nsid, ResourceUri } from "@atcute/lexicons/syntax";import type { OAuthUserAgent } from "@atcute/oauth-browser-client";import { awaitDeletedRecord, awaitIndexedRecord } from "$lib/api/awaitRecord";import { serviceClient } from "$lib/api/_request";import { createBobbinClient } from "$lib/api/client";import { pingIndexNow } from "$lib/api/indexnow";import { mainSchema as addCollaboratorSchema } from "$lib/api/lexicons/types/sh/tangled/repo/addCollaborator";import { mainSchema as deleteRepoSchema, type $input as DeleteRepoInput} from "$lib/api/lexicons/types/sh/tangled/repo/delete";import { mainSchema as removeCollaboratorSchema } from "$lib/api/lexicons/types/sh/tangled/repo/removeCollaborator";import { mainSchema as setDefaultBranchSchema } from "$lib/api/lexicons/types/sh/tangled/repo/setDefaultBranch";import type { RecordView, RepoRecord } from "$lib/api/records";import { rkeyFromUri } from "$lib/api/uri";import { deleteRecord, putRecord, type WrittenRecord } from "$lib/api/write";import { didOf, recordKeyOf } from "$lib/api/syntax";
const REPO_COLLECTION = "sh.tangled.repo" as Nsid;
export interface RepoRecordEdit { name?: string; description?: string; website?: string; topics?: readonly string[]; spindle?: string; labels?: readonly string[];}
const empty = (value: unknown): boolean => value === undefined || value === "" || (Array.isArray(value) && value.length === 0);
export const editedRepoRecord = (record: RepoRecord, edit: RepoRecordEdit): RepoRecord => Object.fromEntries( Object.entries({ ...record, ...edit, $type: "sh.tangled.repo" }).filter( ([, value]) => !empty(value) ) ) as RepoRecord;
export const saveRepoRecord = async ( agent: OAuthUserAgent, bobbinUrl: string, view: RecordView<RepoRecord>, edit: RepoRecordEdit): Promise<WrittenRecord> => { const written = await putRecord( agent, REPO_COLLECTION, rkeyFromUri(view.uri), editedRepoRecord(view.value, edit), view.cid ); await awaitIndexedRecord(createBobbinClient({ serviceUrl: bobbinUrl }), written); return written;};
export const setDefaultBranch = async ( agent: OAuthUserAgent, knot: string, repoDid: string, defaultBranch: string): Promise<void> => { await ok( serviceClient(agent, knot).xrpc.call(setDefaultBranchSchema, { input: { repo: didOf(repoDid), defaultBranch } }) );};
export const addCollaborator = async ( agent: OAuthUserAgent, knot: string, repoDid: string, subject: string): Promise<void> => { await ok( serviceClient(agent, knot).xrpc.call(addCollaboratorSchema, { input: { repo: didOf(repoDid), subject: didOf(subject) } }) );};
export const removeCollaborator = async ( agent: OAuthUserAgent, knot: string, repoDid: string, subject: string): Promise<void> => { await ok( serviceClient(agent, knot).xrpc.call(removeCollaboratorSchema, { input: { repo: didOf(repoDid), subject: didOf(subject) } }) );};
const isRecordGone = (cause: unknown): boolean => cause instanceof ClientResponseError && (cause.status === 404 || cause.error === "RecordNotFound");
export interface RepoLocation { uri: string; knot: string; name: string; ownerHandle: string; ownerDid: string; repoDid?: string; cid?: Cid;}
export const deleteRepo = async ( agent: OAuthUserAgent, bobbinUrl: string, repo: RepoLocation): Promise<void> => { const rkey = rkeyFromUri(repo.uri); try { await deleteRecord(agent, REPO_COLLECTION, rkey, repo.cid); } catch (cause) { if (!isRecordGone(cause)) throw cause; } try { await ok( serviceClient(agent, repo.knot).xrpc.post(deleteRepoSchema.nsid, { as: null, input: { did: didOf(repo.ownerDid), name: repo.name, rkey: recordKeyOf(rkey), ...(repo.repoDid ? { repo: didOf(repo.repoDid) } : {}) } as DeleteRepoInput }) ); } catch (cause) { const detail = cause instanceof Error ? cause.message : String(cause); throw new Error( `The record is gone, but ${repo.knot} did not delete ${repo.name}: ${detail}`, { cause } ); } const confirmed = await awaitDeletedRecord( createBobbinClient({ serviceUrl: bobbinUrl }), repo.uri as ResourceUri ); if (!confirmed) { throw new Error("Deleted, but the appview has not caught up yet."); } pingIndexNow(`https://tangled.org/${repo.ownerHandle}/${repo.name}`);};