const pdsCache = new Map(); let resolvePromise: Promise | null = null; const pendingDids = new Set(); export function getPdsUrlForDid(did: string): string | null { return pdsCache.get(did) ?? null; } export function setPdsForDid(did: string, pdsUrl: string): void { pdsCache.set(did, pdsUrl.replace(/\/$/, '')); } export async function resolvePdsForDids(dids: string[], timeoutMs = 10000): Promise { if (!dids || dids.length === 0) return; const uncached = dids.filter((d) => !pdsCache.has(d) && !pendingDids.has(d)); if (uncached.length === 0) return; for (const did of uncached) pendingDids.add(did); if (resolvePromise) { try { await Promise.race([ resolvePromise, new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeoutMs)), ]); } catch { } const stillUncached = dids.filter((d) => !pdsCache.has(d)); if (stillUncached.length === 0) return; return resolvePdsForDids(stillUncached, timeoutMs); } const toFetch = [...pendingDids]; resolvePromise = (async () => { try { const batchSize = 10; for (let i = 0; i < toFetch.length; i += batchSize) { const batch = toFetch.slice(i, i + batchSize); const results = await Promise.allSettled( batch.map((did) => resolveDidToPds(did)), ); for (let j = 0; j < results.length; j++) { const result = results[j]; if (result.status === 'fulfilled' && result.value) { pdsCache.set(batch[j], result.value); } } } } catch { } finally { pendingDids.clear(); resolvePromise = null; } })(); try { await Promise.race([ resolvePromise, new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeoutMs)), ]); } catch { // Resolution timed out; URL rewriting will proceed without PDS URLs } } export function getPdsCacheSize(): number { return pdsCache.size; } export function clearPdsCache(): void { pdsCache.clear(); } interface DidDocument { id?: string; service?: Array<{ id: string; type: string; serviceEndpoint: string; }>; } function fetchWithTimeout(url: string, timeoutMs: number): Promise { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); return fetch(url, { signal: controller.signal }).finally(() => clearTimeout(timer)); } async function resolveDidToPds(did: string): Promise { try { let doc: DidDocument; if (did.startsWith('did:plc:')) { const resp = await fetchWithTimeout(`https://plc.directory/${encodeURIComponent(did)}`, 5000); if (!resp.ok) return null; doc = await resp.json(); } else if (did.startsWith('did:web:')) { const domain = did.slice('did:web:'.length); const resp = await fetchWithTimeout(`https://${domain}/.well-known/did.json`, 5000); if (!resp.ok) return null; doc = await resp.json(); } else { return null; } const pdsService = doc.service?.find( (s) => s.id === '#atproto_pds' || s.type === 'AtprotoPersonalDataServer', ); if (pdsService?.serviceEndpoint) { return pdsService.serviceEndpoint.replace(/\/$/, ''); } return null; } catch { return null; } }