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