Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163import { ok } from "@atcute/client";import { mainSchema as getRecordSchema } from "@atcute/atproto/types/repo/getRecord";import type { Nsid, RecordKey } from "@atcute/lexicons/syntax";import type { OAuthUserAgent } from "@atcute/oauth-browser-client";import { now as tidNow } from "@atcute/tid";import { createClient, mintServiceAuth, serviceDidForHost } from "$lib/auth/agent";import { deleteRecord, putRecord } from "$lib/api/write";import { buildUrl, toResponseError } from "$lib/api/_request";import type { PullRecord, PullState, PullStatusRecord, RecordView } from "$lib/api/records";
const PULL_COLLECTION = "sh.tangled.repo.pull" as Nsid;const PULL_STATUS_COLLECTION = "sh.tangled.repo.pull.status" as Nsid;
export const putPull = async ( agent: OAuthUserAgent, rkey: string, record: PullRecord): Promise<RecordView<PullRecord>> => { const { uri, cid } = await putRecord(agent, PULL_COLLECTION, rkey, record); return { uri, cid, value: record };};
/** read-modify-write against the live record */const updatePull = async ( agent: OAuthUserAgent, rkey: string, mutate: (record: PullRecord) => PullRecord): Promise<RecordView<PullRecord>> => { const rpc = createClient(agent); const existing = await ok( rpc.call(getRecordSchema, { params: { repo: agent.sub, collection: PULL_COLLECTION, rkey: rkey as RecordKey } }) ); const record = mutate(existing.value as PullRecord); const { uri, cid } = await putRecord(agent, PULL_COLLECTION, rkey, record, existing.cid); return { uri, cid, value: record };};
export const editPull = ( agent: OAuthUserAgent, rkey: string, ticket: { title: string; body: string }): Promise<RecordView<PullRecord>> => updatePull(agent, rkey, (record) => ({ ...record, ...ticket }));
export const resubmitPull = ( agent: OAuthUserAgent, rkey: string, version: PullRecord["versions"][number]): Promise<RecordView<PullRecord>> => updatePull(agent, rkey, (record) => ({ ...record, versions: [...(record.versions ?? []), version] }));
export const deletePull = async (agent: OAuthUserAgent, rkey: string): Promise<void> => { await deleteRecord(agent, PULL_COLLECTION, rkey);};
/** * Build the status record for a pull transition. Statuses are append-only: every transition is * a fresh record at a new tid, and readers take the newest one they trust. */export const pullStatusRecord = (pull: string, status: PullState): PullStatusRecord => ({ $type: "sh.tangled.repo.pull.status", pull: pull as PullStatusRecord["pull"], status: `sh.tangled.repo.pull.status.${status}`, createdAt: new Date().toISOString() as PullStatusRecord["createdAt"]});
/** Writes into the acting user's repo, not the pull author's. */export const putPullStatus = async ( agent: OAuthUserAgent, pull: string, status: PullState): Promise<RecordView<PullStatusRecord>> => { const record = pullStatusRecord(pull, status); const { uri, cid } = await putRecord(agent, PULL_STATUS_COLLECTION, tidNow(), record); return { uri, cid, value: record };};const KEEP_COMMIT_NSID = "sh.tangled.git.keepCommit";
export interface KeepCommitInput { /** repo did the commit lives in */ repo: string; /** the commit to pin */ oid: string; /** at-uri of the record that owns the keep ref; the rkey has to be final */ record: string;}
export const keepCommit = async ( agent: OAuthUserAgent, knot: string, input: KeepCommitInput, fetch: typeof globalThis.fetch = globalThis.fetch): Promise<{ commit: string }> => { const token = await mintServiceAuth(agent, { aud: serviceDidForHost(knot), lxm: KEEP_COMMIT_NSID }); const response = await fetch(`https://${knot}/xrpc/${KEEP_COMMIT_NSID}`, { method: "POST", headers: { "content-type": "application/json", accept: "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify({ repo: input.repo, record: input.record, source: { $type: `${KEEP_COMMIT_NSID}#commit`, repo: input.repo, oid: input.oid } }) }); if (!response.ok) throw await toResponseError(response); return (await response.json()) as { commit: string };};
const MERGE_COMMIT_NSID = "sh.tangled.git.mergeCommit";const REPO_PUSH_NSID = "sh.tangled.repo.push";
export interface MergeCommitInput { /** the repo and branch being merged into */ target: { repo: string; branch: string; commit: string }; /** the repo the head commit lives in, and the commit itself */ source: { repo: string; commit: string }; style: "rebase";}
export const mergeCommit = async ( agent: OAuthUserAgent, mirror: string, knot: string, input: MergeCommitInput, fetch: typeof globalThis.fetch = globalThis.fetch): Promise<void> => { const token = await mintServiceAuth(agent, { aud: serviceDidForHost(knot), // `sh.tangled.git.mergeCommit` expects service-auth token minted for `sh.tangled.repo.push` // so it can reuse it on git-push. lxm: REPO_PUSH_NSID }); const response = await fetch(buildUrl(mirror, MERGE_COMMIT_NSID), { method: "POST", headers: { "content-type": "application/json", accept: "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify({ target: { $type: `${MERGE_COMMIT_NSID}#target`, ...input.target }, source: { $type: `${MERGE_COMMIT_NSID}#source`, ...input.source }, style: input.style }) }); if (!response.ok) throw await toResponseError(response);};