/** * Finding the server that holds an account's data. * * A DID document says where a repository lives, and reading a record or a * blob out of that repository needs no account and no token — a PDS answers * both to anyone, with `access-control-allow-origin: *`. So anything this site * only reads, it reads straight from the source rather than through our API. * * The alternative, asking an appview, is one fewer request and worse: it only * knows accounts it has indexed, and its CDN sends no CORS header at all, so a * canvas that draws from it cannot be read back. */ /** * What a DID document says that anything here reads. * * `alsoKnownAs` is the account's own claim to a handle, and it is only a * claim: identity.ts is what checks the handle resolves back to this DID * before anyone is shown it. */ export interface DidDocument { readonly alsoKnownAs?: unknown; readonly service?: { id?: string; serviceEndpoint?: unknown }[]; } const cache = new Map>(); /** * A DID's document. Cached: it does not move often, and both the server it * names and the handle it claims are read off the one fetch. */ export function didDocument(did: string): Promise { const known = cache.get(did); if (known) return known; const pending = lookup(did).catch((error: unknown) => { // A failure must not be remembered, or one flaky lookup breaks the page // until it is reloaded. cache.delete(did); throw error; }); cache.set(did, pending); return pending; } /** The PDS endpoint for a DID. */ export async function resolvePds(did: string): Promise { const doc = await didDocument(did); const endpoint = doc.service?.find((s) => s.id?.endsWith("#atproto_pds"), )?.serviceEndpoint; if (typeof endpoint !== "string") { throw new Error("Your account's identity names no server."); } return endpoint.replace(/\/+$/, ""); } async function lookup(did: string): Promise { let docUrl: string; if (did.startsWith("did:plc:")) { docUrl = `https://plc.directory/${encodeURIComponent(did)}`; } else if (did.startsWith("did:web:")) { // did:web:example.com is the well-known path; extra colon-separated // segments are a path instead. A port arrives percent-encoded. const [host, ...path] = did.slice("did:web:".length).split(":"); const where = path.length ? `/${path.join("/")}` : "/.well-known"; docUrl = `https://${decodeURIComponent(host ?? "")}${where}/did.json`; } else { throw new Error("Your account's identity is not one we can read."); } const response = await fetch(docUrl); if (!response.ok) throw new Error("Your account's identity did not resolve."); return (await response.json()) as DidDocument; } /** The public URL of a blob in someone's repository. */ export function blobUrl(pds: string, did: string, cid: string): string { return `${pds}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`; }