~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
62 lines · 1.8 KB Raw History
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<string[]>} - 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 [];
  }
}