this repo has no description
at filter-styling 173 lines 4.4 kB view raw
1import "@atcute/atproto"; 2import { 3 type DidDocument, 4 getLabelerEndpoint, 5 getPdsEndpoint, 6 isAtprotoDid, 7} from "@atcute/identity"; 8import { 9 AtprotoWebDidDocumentResolver, 10 CompositeDidDocumentResolver, 11 CompositeHandleResolver, 12 DohJsonHandleResolver, 13 PlcDidDocumentResolver, 14 WellKnownHandleResolver, 15 XrpcHandleResolver, 16} from "@atcute/identity-resolver"; 17import { Did, Handle } from "@atcute/lexicons"; 18import { isHandle } from "@atcute/lexicons/syntax"; 19import { createStore } from "solid-js/store"; 20import { setPDS } from "../components/navbar"; 21 22const didDocumentResolver = new CompositeDidDocumentResolver({ 23 methods: { 24 plc: new PlcDidDocumentResolver({ 25 apiUrl: localStorage.plcDirectory ?? "https://plc.directory", 26 }), 27 web: new AtprotoWebDidDocumentResolver(), 28 }, 29}); 30 31const handleResolver = new XrpcHandleResolver({ 32 serviceUrl: "https://public.api.bsky.app", 33}); 34 35const didPDSCache: Record<string, string> = {}; 36const [labelerCache, setLabelerCache] = createStore<Record<string, string>>({}); 37const didDocCache: Record<string, DidDocument> = {}; 38const getPDS = async (did: string) => { 39 if (did in didPDSCache) return didPDSCache[did]; 40 41 if (!isAtprotoDid(did)) { 42 throw new Error("Not a valid DID identifier"); 43 } 44 45 const doc = await didDocumentResolver.resolve(did); 46 didDocCache[did] = doc; 47 48 const pds = getPdsEndpoint(doc); 49 const labeler = getLabelerEndpoint(doc); 50 51 if (labeler) { 52 setLabelerCache(did, labeler); 53 } 54 55 if (!pds) { 56 throw new Error("No PDS found"); 57 } 58 59 return (didPDSCache[did] = pds); 60}; 61 62const resolveHandle = async (handle: Handle) => { 63 if (!isHandle(handle)) { 64 throw new Error("Not a valid handle"); 65 } 66 67 return await handleResolver.resolve(handle); 68}; 69 70const resolveDidDoc = async (did: Did) => { 71 if (!isAtprotoDid(did)) { 72 throw new Error("Not a valid DID identifier"); 73 } 74 return await didDocumentResolver.resolve(did); 75}; 76 77const validateHandle = async (handle: Handle, did: Did) => { 78 if (!isHandle(handle)) return false; 79 80 const handleResolver = new CompositeHandleResolver({ 81 strategy: "dns-first", 82 methods: { 83 dns: new DohJsonHandleResolver({ dohUrl: "https://dns.google/resolve?" }), 84 http: new WellKnownHandleResolver(), 85 }, 86 }); 87 88 let resolvedDid: string; 89 try { 90 resolvedDid = await handleResolver.resolve(handle); 91 } catch (err) { 92 console.error(err); 93 return false; 94 } 95 if (resolvedDid !== did) return false; 96 return true; 97}; 98 99const resolvePDS = async (did: string) => { 100 setPDS(undefined); 101 const pds = await getPDS(did); 102 if (!pds) throw new Error("No PDS found"); 103 setPDS(pds.replace("https://", "").replace("http://", "")); 104 return pds; 105}; 106 107interface LinkData { 108 links: { 109 [key: string]: { 110 [key: string]: { 111 records: number; 112 distinct_dids: number; 113 }; 114 }; 115 }; 116} 117 118const getConstellation = async ( 119 endpoint: string, 120 target: string, 121 collection?: string, 122 path?: string, 123 cursor?: string, 124 limit?: number, 125) => { 126 const url = new URL("https://constellation.microcosm.blue"); 127 url.pathname = endpoint; 128 url.searchParams.set("target", target); 129 if (collection) { 130 if (!path) throw new Error("collection and path must either both be set or neither"); 131 url.searchParams.set("collection", collection); 132 url.searchParams.set("path", path); 133 } else { 134 if (path) throw new Error("collection and path must either both be set or neither"); 135 } 136 if (limit) url.searchParams.set("limit", `${limit}`); 137 if (cursor) url.searchParams.set("cursor", `${cursor}`); 138 const res = await fetch(url, { signal: AbortSignal.timeout(5000) }); 139 if (!res.ok) throw new Error("failed to fetch from constellation"); 140 return await res.json(); 141}; 142 143const getAllBacklinks = (target: string) => getConstellation("/links/all", target); 144 145const getRecordBacklinks = ( 146 target: string, 147 collection: string, 148 path: string, 149 cursor?: string, 150 limit?: number, 151) => getConstellation("/links", target, collection, path, cursor, limit || 100); 152 153const getDidBacklinks = ( 154 target: string, 155 collection: string, 156 path: string, 157 cursor?: string, 158 limit?: number, 159) => getConstellation("/links/distinct-dids", target, collection, path, cursor, limit || 100); 160 161export { 162 didDocCache, 163 getAllBacklinks, 164 getDidBacklinks, 165 getPDS, 166 getRecordBacklinks, 167 labelerCache, 168 resolveDidDoc, 169 resolveHandle, 170 resolvePDS, 171 validateHandle, 172 type LinkData, 173};