atproto explorer forked from pds.ls/pdsls
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204import "@atcute/atproto";import { type DidDocument, getLabelerEndpoint, getPdsEndpoint, isAtprotoDid,} from "@atcute/identity";import { AtprotoWebDidDocumentResolver, CompositeDidDocumentResolver, CompositeHandleResolver, DohJsonHandleResolver, PlcDidDocumentResolver, WellKnownHandleResolver,} from "@atcute/identity-resolver";import { DohJsonLexiconAuthorityResolver, LexiconSchemaResolver } from "@atcute/lexicon-resolver";import { Did, Handle } from "@atcute/lexicons";import { AtprotoDid, isHandle, Nsid } from "@atcute/lexicons/syntax";import { createStore } from "solid-js/store";import { setPDS } from "../components/navbar";
export const didDocumentResolver = new CompositeDidDocumentResolver({ methods: { plc: new PlcDidDocumentResolver({ apiUrl: localStorage.getItem("plcDirectory") ?? "https://plc.directory", }), web: new AtprotoWebDidDocumentResolver(), },});
export const handleResolver = new CompositeHandleResolver({ strategy: "dns-first", methods: { dns: new DohJsonHandleResolver({ dohUrl: "https://dns.google/resolve?" }), http: new WellKnownHandleResolver(), },});
const authorityResolver = new DohJsonLexiconAuthorityResolver({ dohUrl: "https://mozilla.cloudflare-dns.com/dns-query",});
const schemaResolver = new LexiconSchemaResolver({ didDocumentResolver: didDocumentResolver,});
const didPDSCache: Record<string, string> = {};const [labelerCache, setLabelerCache] = createStore<Record<string, string>>({});const didDocCache: Record<string, DidDocument> = {};const getPDS = async (did: string) => { if (did in didPDSCache) return didPDSCache[did];
if (!isAtprotoDid(did)) { throw new Error("Not a valid DID identifier"); }
const doc = await didDocumentResolver.resolve(did); didDocCache[did] = doc;
const pds = getPdsEndpoint(doc); const labeler = getLabelerEndpoint(doc);
if (labeler) { setLabelerCache(did, labeler); }
if (!pds) { throw new Error("No PDS found"); }
return (didPDSCache[did] = pds);};
const resolveHandle = async (handle: Handle) => { if (!isHandle(handle)) { throw new Error("Not a valid handle"); }
return await handleResolver.resolve(handle);};
const resolveDidDoc = async (did: Did) => { if (!isAtprotoDid(did)) { throw new Error("Not a valid DID identifier"); } return await didDocumentResolver.resolve(did);};
const validateHandle = async (handle: Handle, did: Did) => { if (!isHandle(handle)) return false;
let resolvedDid: string; try { resolvedDid = await handleResolver.resolve(handle); } catch (err) { console.error(err); return false; } if (resolvedDid !== did) return false; return true;};
const resolvePDS = async (did: string) => { setPDS(undefined); const pds = await getPDS(did); if (!pds) throw new Error("No PDS found"); setPDS(pds.replace("https://", "").replace("http://", "")); return pds;};
const resolveLexiconAuthority = async (nsid: Nsid) => { return await authorityResolver.resolve(nsid);};
const resolveLexiconSchema = async (authority: AtprotoDid, nsid: Nsid) => { return await schemaResolver.resolve(authority, nsid);};
interface LinkData { links: { [key: string]: { [key: string]: { records: number; distinct_dids: number; }; }; };}
type LinksWithRecords = { cursor: string; total: number; linking_records: Array<{ did: string; collection: string; rkey: string }>;};
type LinksWithDids = { cursor: string; total: number; linking_dids: Array<string>;};
const getConstellation = async ( endpoint: string, target: string, collection?: string, path?: string, cursor?: string, limit?: number,) => { const url = new URL("https://constellation.microcosm.blue"); url.pathname = endpoint; url.searchParams.set("target", target); if (collection) { if (!path) throw new Error("collection and path must either both be set or neither"); url.searchParams.set("collection", collection); url.searchParams.set("path", path); } else { if (path) throw new Error("collection and path must either both be set or neither"); } if (limit) url.searchParams.set("limit", `${limit}`); if (cursor) url.searchParams.set("cursor", `${cursor}`); const res = await fetch(url, { signal: AbortSignal.timeout(5000) }); if (!res.ok) throw new Error("failed to fetch from constellation"); return await res.json();};
const getAllBacklinks = (target: string) => getConstellation("/links/all", target);
const getRecordBacklinks = ( target: string, collection: string, path: string, cursor?: string, limit?: number,): Promise<LinksWithRecords> => getConstellation("/links", target, collection, path, cursor, limit || 100);
const getDidBacklinks = ( target: string, collection: string, path: string, cursor?: string, limit?: number,): Promise<LinksWithDids> => getConstellation("/links/distinct-dids", target, collection, path, cursor, limit || 100);
export { didDocCache, getAllBacklinks, getDidBacklinks, getPDS, getRecordBacklinks, labelerCache, resolveDidDoc, resolveHandle, resolveLexiconAuthority, resolveLexiconSchema, resolvePDS, validateHandle, type LinkData, type LinksWithDids, type LinksWithRecords,};