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<AtprotoDid> => {
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<HandleResolver["resolve"]>) {
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}` })
)
);