This repository has no description
1import { ok } from "@atcute/client";
2import { mainSchema as createRecordSchema } from "@atcute/atproto/types/repo/createRecord";
3import { mainSchema as deleteRecordSchema } from "@atcute/atproto/types/repo/deleteRecord";
4import type { Did, Nsid, RecordKey } from "@atcute/lexicons/syntax";
5import type { OAuthUserAgent } from "@atcute/oauth-browser-client";
6import { createClient } from "$lib/auth/agent";
7import { ClientResponseError, type BobbinContext } from "./client";
8import { jsonGet } from "./_request";
9import { httpStatusFor } from "./load";
10import { rkeyFromUri } from "./uri";
11import type * as ShTangledGraphFollow from "./lexicons/types/sh/tangled/graph/follow";
12import type * as ShTangledGraphGetFollow from "./lexicons/types/sh/tangled/graph/getFollow";
13import type * as ShTangledGraphVouch from "./lexicons/types/sh/tangled/graph/vouch";
14
15export type FollowRecord = ShTangledGraphFollow.Main;
16export type VouchRecord = ShTangledGraphVouch.Main;
17
18const FOLLOW_COLLECTION = "sh.tangled.graph.follow" as Nsid;
19const STAR_COLLECTION = "sh.tangled.feed.star" as Nsid;
20
21// 404 means no follow exists, other errors propagate
22export const getFollowRkey = async (
23 ctx: BobbinContext,
24 actor: string,
25 subject: string
26): Promise<string | null> => {
27 try {
28 const { uri } = await jsonGet<ShTangledGraphGetFollow.$output>(
29 ctx,
30 "sh.tangled.graph.getFollow",
31 { actor: actor as Did, subject: subject as Did } satisfies ShTangledGraphGetFollow.$params
32 );
33 return rkeyFromUri(uri);
34 } catch (cause) {
35 if (cause instanceof ClientResponseError && httpStatusFor(cause) === 404) return null;
36 throw cause;
37 }
38};
39
40const createGenericRecord = async <T extends { $type: string }>(
41 agent: OAuthUserAgent,
42 collection: Nsid,
43 record: T
44): Promise<string> => {
45 const rpc = createClient(agent);
46 const result = await ok(
47 rpc.call(createRecordSchema, {
48 input: { repo: agent.sub, collection, record }
49 })
50 );
51 return rkeyFromUri(result.uri);
52};
53
54const deleteGenericRecord = async (
55 agent: OAuthUserAgent,
56 collection: Nsid,
57 rkey: string
58): Promise<void> => {
59 const rpc = createClient(agent);
60 await ok(
61 rpc.call(deleteRecordSchema, {
62 input: { repo: agent.sub, collection, rkey: rkey as RecordKey }
63 })
64 );
65};
66
67export const createFollow = (agent: OAuthUserAgent, subject: string): Promise<string> =>
68 createGenericRecord(agent, FOLLOW_COLLECTION, {
69 $type: "sh.tangled.graph.follow",
70 subject: subject as Did,
71 createdAt: new Date().toISOString()
72 });
73
74export const deleteFollow = (agent: OAuthUserAgent, rkey: string): Promise<void> =>
75 deleteGenericRecord(agent, FOLLOW_COLLECTION, rkey);
76
77export const createStar = (agent: OAuthUserAgent, repoDid: string): Promise<string> =>
78 createGenericRecord(agent, STAR_COLLECTION, {
79 $type: "sh.tangled.feed.star",
80 subject: {
81 $type: "sh.tangled.feed.star#repo",
82 did: repoDid as Did
83 },
84 createdAt: new Date().toISOString()
85 });
86
87export const deleteStar = (agent: OAuthUserAgent, rkey: string): Promise<void> =>
88 deleteGenericRecord(agent, STAR_COLLECTION, rkey);