import { FETCH_TIMEOUT_MS, isPublicHttpsUrl } from './net.ts'; import type { Upstream } from './upstream.ts'; /** Where a DID's document lives: `plc.directory` for `did:plc:`, and for `did:web:` the host * (with `%3A` for a port) plus any `:`-separated path, per the did:web spec. `undefined` for any * other method or a host that isn't safe to fetch. */ export function didDocumentUrl(did: string): string | undefined { if (/^did:plc:[a-z2-7]{24}$/.test(did)) return `https://plc.directory/${did}`; if (!did.startsWith('did:web:')) return undefined; const [host, ...path] = did.slice('did:web:'.length).split(':'); if (!host) return undefined; const url = path.length > 0 ? `https://${decodeURIComponent(host)}/${path.map(decodeURIComponent).join('/')}/did.json` : `https://${decodeURIComponent(host)}/.well-known/did.json`; return isPublicHttpsUrl(url) ? url : undefined; } /** Resolves a DID to its PDS endpoint. Throws when the document can't be fetched or names no * PDS, or names one that isn't a public `https:` URL. */ export async function resolveDidPds(did: string, fetchFn: Upstream, timeoutMs = FETCH_TIMEOUT_MS): Promise { const docUrl = didDocumentUrl(did); if (!docUrl) throw new Error(`cannot resolve ${did}`); const res = await fetchFn(docUrl, { signal: AbortSignal.timeout(timeoutMs) }); if (!res.ok) throw new Error(`DID document for ${did}: HTTP ${res.status}`); const pds = serviceEndpoint(await res.json(), did, { type: 'AtprotoPersonalDataServer' }); if (!pds) throw new Error(`no usable PDS in DID document for ${did}`); return pds; } /** The endpoint of the first service in `doc` with this `id` (`#atproto_pds`, `#tangled_knot`) or * `type`, when it is a public `https:` URL. A DID document is untrusted input. */ export function serviceEndpoint(doc: unknown, did: string, match: { id?: `#${string}`; type?: string }): string | undefined { const raw = (doc as { service?: unknown } | null)?.service; const services = Array.isArray(raw) ? (raw as { id?: unknown; type?: unknown; serviceEndpoint?: unknown }[]) : []; const found = services.find((s) => match.id ? s?.id === match.id || s?.id === `${did}${match.id}` : s?.type === match.type, )?.serviceEndpoint; return typeof found === 'string' && isPublicHttpsUrl(found) ? found : undefined; }