import { ensureAtpDocument, IdResolver } from '@atproto/identity'; /** * Resolve a handle to a DID using AT Protocol identity resolution. * Uses @atproto/identity directly rather than routing through the authenticated * agent, so the result is independent of which PDS the caller is logged into. * * @param handle - Handle string (e.g., "mark.bsky.social" or "@mark.bsky.social") * @returns DID string (e.g., "did:plc:abc123") * @throws Error if handle cannot be resolved */ export async function resolveHandleToDid(handle: string): Promise { // Strip leading @ if present const cleanHandle = handle.startsWith('@') ? handle.slice(1) : handle; try { const resolver = new IdResolver(); const did = await resolver.handle.resolve(cleanHandle); if (!did) { throw new Error(`No DID found for handle: ${cleanHandle}`); } return did; } catch (error) { if (error instanceof Error && error.message.startsWith('No DID found')) { throw error; } if (error instanceof Error) { throw new Error(`Failed to resolve handle '${cleanHandle}': ${error.message}`); } throw new Error(`Failed to resolve handle '${cleanHandle}': Unknown error`); } } export async function resolvePdsFromIdentifier(identifier: string): Promise { const resolver = new IdResolver(); let did: string; if (identifier.startsWith('did:')) { did = identifier; } else { const resolved = await resolver.handle.resolve(identifier); if (!resolved) { throw new Error(`Could not resolve handle "${identifier}" to a DID`); } did = resolved; } // resolveNoCheck fetches the DID document without verifying its signature. // Acceptable for a CLI that trusts the DNS/HTTPS infrastructure used for // handle resolution; avoids the overhead of full cryptographic verification. const didDoc = await resolver.did.resolveNoCheck(did); // Cast needed because resolveNoCheck returns a broader type than ensureAtpDocument expects. const atpData = ensureAtpDocument(didDoc as Parameters[0]); return atpData.pds; }