import { CompositeHandleResolver, DohJsonHandleResolver, FailedHandleResolutionError, WellKnownHandleResolver, XrpcHandleResolver, type HandleResolver } from "@atcute/identity-resolver"; import type { AtprotoDid } from "@atcute/lexicons/syntax"; import { createBobbinClient, type BobbinContext } from "#lib/api/client.ts"; import { fetchMiniDoc } from "#lib/api/identity.ts"; export const handleResolverUrls = (raw: string | undefined): string[] => (raw ?? "") .split(",") .map((url) => url.trim().replace(/\/+$/, "")) .filter(Boolean); const handleResolverFromTheNetwork = (bobbinUrl: () => string): HandleResolver => new FallbackHandleResolver([ new CompositeHandleResolver({ methods: { dns: new DohJsonHandleResolver({ dohUrl: "https://cloudflare-dns.com/dns-query" }), http: new WellKnownHandleResolver() } }), bobbinHandleResolver(bobbinUrl) ]); const fetchHandleDid = async ( ctx: BobbinContext, handle: string, signal?: AbortSignal ): Promise => { const doc = await fetchMiniDoc(ctx, handle, { signal }); return doc.did as AtprotoDid; }; const bobbinHandleResolver = (bobbinUrl: () => string): HandleResolver => ({ resolve: (handle, options) => fetchHandleDid( createBobbinClient({ serviceUrl: bobbinUrl() }), handle, options?.signal ).catch((cause: unknown) => { throw new FailedHandleResolutionError(handle, { cause }); }) }); export class FallbackHandleResolver implements HandleResolver { readonly #resolvers: HandleResolver[]; constructor(resolvers: HandleResolver[]) { this.#resolvers = resolvers; } async resolve(...args: Parameters) { let failure: unknown; for (const resolver of this.#resolvers) { try { return await resolver.resolve(...args); } catch (cause) { if (args[1]?.signal?.aborted) throw cause; failure = cause; } } throw failure; } } export const handleResolverFor = (urls: string[], bobbinUrl: () => string): HandleResolver => urls.length === 0 ? handleResolverFromTheNetwork(bobbinUrl) : urls.length === 1 ? new XrpcHandleResolver({ serviceUrl: urls[0] as `${string}:${string}` }) : new FallbackHandleResolver( urls.map( (url) => new XrpcHandleResolver({ serviceUrl: url as `${string}:${string}` }) ) );