chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
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 [];
}
}