Something went wrong. Try again.
Two-way sync Firefox bookmarks with an AT Protocol PDS using network.cosmik.* lexicons.
Something went wrong. Try again.
5.9 kB · 172 lines
JavaScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173const DNS_ENDPOINT = "https://cloudflare-dns.com/dns-query";const logResolve = createLogger("resolve");
/** * Resolves an AT Protocol handle to its DID, DID document, and PDS URL. * Uses DNS-over-HTTPS first, falls back to `.well-known/atproto-did.json`. * @param {string} handle - The user's AT Protocol handle (e.g. "user.bsky.social") * @returns {Promise<{did: string, pdsUrl: string, doc: Object}>} */async function resolvePDS(handle) { const done = logResolve.timer("resolvePDS"); const did = await resolveDID(handle); const doc = await resolveDIDDocument(did); const pdsUrl = extractPdsUrl(doc, did); done(`resolved ${handle} -> ${did}`); return { did, pdsUrl, doc };}
/** * Resolves a handle to a DID string via DNS or .well-known. * @param {string} handle - The handle to resolve * @returns {Promise<string>} The resolved DID */async function resolveDID(handle) { const cleaned = handle.replace(/^https?:\/\//, "").replace(/\/$/, "");
const fromDns = await resolveDIDviaDNS(cleaned); if (fromDns) { logResolve.debug("resolveDID via DNS", { handle: cleaned }); return fromDns; }
const fromWellKnown = await resolveDIDviaWellKnown(cleaned); if (fromWellKnown) { logResolve.debug("resolveDID via well-known", { handle: cleaned }); return fromWellKnown; }
logResolve.error("resolveDID failed", { handle }); throw new Error(`Could not resolve handle: ${handle}`);}
/** * Looks up a DID via DNS TXT record at _atproto.<handle> using Cloudflare DoH. * @param {string} handle - The handle to look up * @returns {Promise<string|null>} The DID if found, or null */async function resolveDIDviaDNS(handle) { try { const url = `${DNS_ENDPOINT}?name=_atproto.${encodeURIComponent(handle)}&type=TXT`; logResolve.debug("resolveDIDviaDNS", { handle }); const resp = await fetch(url, { headers: { Accept: "application/dns-json" }, }); if (!resp.ok) { logResolve.debug("DNS query not ok", { status: resp.status }); return null; } const data = await resp.json(); if (!data.Answer) { logResolve.debug("No DNS answer records"); return null; } for (const record of data.Answer) { if (record.type === 16 && record.data) { const txt = record.data.replace(/^"|"$/g, "").trim(); const match = txt.match(/^did=(did:.+)$/); if (match) return match[1]; } } logResolve.debug("No DID record in DNS answer"); return null; } catch (err) { logResolve.debug("DNS resolution failed", { error: err.message }); return null; }}
/** * Fetches a DID from the handle's .well-known/atproto-did.json endpoint. * @param {string} handle - The handle to check * @returns {Promise<string|null>} The DID if found, or null */async function resolveDIDviaWellKnown(handle) { try { const url = `https://${handle}/.well-known/atproto-did.json`; logResolve.debug("resolveDIDviaWellKnown", { handle }); const resp = await fetch(url); if (!resp.ok) { logResolve.debug("Well-known not ok", { status: resp.status }); return null; } const data = await resp.json(); if (data.did) return data.did; logResolve.debug("No DID in well-known response"); return null; } catch (err) { logResolve.debug("Well-known fetch failed", { error: err.message }); return null; }}
/** * Fetches a DID document for a given DID (supports did:plc and did:web). * @param {string} did - The DID to resolve (e.g. "did:plc:abc123" or "did:web:example.com") * @returns {Promise<Object>} The DID document */async function resolveDIDDocument(did) { logResolve.debug("resolveDIDDocument", { did }); if (did.startsWith("did:plc:")) { return resolvePLCDID(did); } if (did.startsWith("did:web:")) { return resolveWebDID(did); } logResolve.error("Unsupported DID method", { did }); throw new Error(`Unsupported DID method: ${did}`);}
/** * Fetches a DID document from plc.directory for a did:plc identifier. * @param {string} did - The did:plc identifier * @returns {Promise<Object>} The PLC DID document */async function resolvePLCDID(did) { const url = `https://plc.directory/${encodeURIComponent(did)}`; logResolve.debug("resolvePLCDID", { did }); const done = logResolve.timer("PLC directory fetch"); const resp = await fetch(url); done(`PLC directory responded ${resp.status}`); if (!resp.ok) { throw new Error(`PLC directory returned HTTP ${resp.status} for ${did}`); } return resp.json();}
/** * Fetches a DID document from the domain's .well-known/did.json for a did:web identifier. * @param {string} did - The did:web identifier (e.g. "did:web:example.com") * @returns {Promise<Object>} The Web DID document */async function resolveWebDID(did) { const domain = did.replace(/^did:web:/, "").replace(/:/g, "/"); const url = `https://${domain}/.well-known/did.json`; logResolve.debug("resolveWebDID", { did, domain }); const resp = await fetch(url); if (!resp.ok) { throw new Error(`Web DID document not found at ${url}`); } return resp.json();}
/** * Extracts the PDS URL from a DID document's service entries. * Looks for a service with type "AtprotoPersonalDataServer". * @param {Object} doc - The DID document * @param {string} did - The DID (used for error messages) * @returns {string} The PDS base URL */function extractPdsUrl(doc, did) { if (!doc || !doc.service) { logResolve.error("No services in DID document", { did }); throw new Error(`No services in DID document for ${did}`); } for (const svc of doc.service) { if (svc.type === "AtprotoPersonalDataServer" && svc.serviceEndpoint) { return svc.serviceEndpoint.replace(/\/+$/, ""); } } logResolve.error("No AtprotoPersonalDataServer service", { did }); throw new Error(`No AtprotoPersonalDataServer service in DID document for ${did}`);}