import { isValidDid } from '@bigmoves/lexicon'; const DEFAULT_DOH_URL = 'https://cloudflare-dns.com/dns-query'; /** * @typedef {{type: number, data?: string}} DnsRecord */ /** * Parse DID from AT Protocol TXT record value. * Format: "did=did:plc:xyz123" or did=did:plc:xyz123 * Validates DID format using @bigmoves/lexicon. * * @param {string} txt - TXT record value * @returns {string|null} - DID or null if invalid */ export function parseDidFromTxt(txt) { if (!txt) return null; // Remove surrounding quotes if present const unquoted = txt.replace(/^"|"$/g, ''); const match = unquoted.match(/^did=(.+)$/); if (!match) return null; const did = match[1]; return isValidDid(did) ? did : null; } /** * Lookup TXT records via DNS-over-HTTPS. * * @param {string} domain - Domain to lookup (e.g., "_atproto.bsky.app") * @param {Object} [opts] * @param {string} [opts.dohUrl] - DoH endpoint URL * @param {typeof fetch} [opts.fetch] - Fetch implementation * @returns {Promise} - Array of TXT record values */ export async function lookupTxt(domain, opts = {}) { const dohUrl = opts.dohUrl ?? DEFAULT_DOH_URL; const fetchFn = opts.fetch ?? globalThis.fetch; try { const url = `${dohUrl}?name=${encodeURIComponent(domain)}&type=TXT`; const response = await fetchFn(url, { headers: { Accept: 'application/dns-json' }, }); if (!response.ok) return []; const data = await response.json(); if (!data.Answer) return []; // Extract TXT record values (type 16 = TXT) return data.Answer.filter( (/** @type {DnsRecord} */ record) => record.type === 16, ).map((/** @type {DnsRecord} */ record) => { // Remove surrounding quotes from data field const value = record.data?.replace(/^"|"$/g, '') ?? ''; return value; }); } catch { return []; } }