import type * as BlueMicrocosmIdentityResolveMiniDoc from "@atcute/microcosm/types/blue/microcosm/identity/resolveMiniDoc";
import { isDid } from "@atcute/lexicons/syntax";
import { cached } from "#lib/api/cache.ts";
import type { BobbinContext, XrpcRequestInit } from "#lib/api/client.ts";
import { jsonGet } from "#lib/api/_request.ts";
import { didTag, handleTag, identifierTag, lookups } from "#lib/api/lookupCache.ts";
export type MiniDoc = BlueMicrocosmIdentityResolveMiniDoc.$output;
export { INVALID_HANDLE, canonicalHandle, profileIdentifier } from "#lib/api/profileIdentifier.ts";
export const fetchMiniDoc = (
ctx: BobbinContext,
identifier: string,
init?: XrpcRequestInit
): Promise<MiniDoc> =>
jsonGet<MiniDoc>(ctx, "blue.microcosm.identity.resolveMiniDoc", { identifier }, init);
export const resolveMiniDoc = (
ctx: BobbinContext,
identifier: string,
init?: XrpcRequestInit
): Promise<MiniDoc> =>
cached(`minidoc:${identifier.toLowerCase()}`, 60_000, () =>
lookups.lookup(
`minidoc:${ctx.serviceUrl}:${identifier.toLowerCase()}`,
[identifierTag(identifier)],
() => fetchMiniDoc(ctx, identifier, init),
(doc) => [didTag(doc.did), handleTag(doc.handle)]
)
);
// did/handle cache with in-flight de-dupe.
export class IdentityCache {
readonly #ctx: BobbinContext;
readonly #byDid = new Map<string, MiniDoc>();
readonly #byHandle = new Map<string, MiniDoc>();
readonly #inflight = new Map<string, Promise<MiniDoc>>();
constructor(ctx: BobbinContext) {
this.#ctx = ctx;
}
prime(doc: MiniDoc): void {
this.#byDid.set(doc.did, doc);
this.#byHandle.set(doc.handle, doc);
}
didFor(handle: string): string | undefined {
return this.#byHandle.get(handle)?.did;
}
handleFor(did: string): string | undefined {
return this.#byDid.get(did)?.handle;
}
resolve(identifier: string, init?: XrpcRequestInit): Promise<MiniDoc> {
const hit = isDid(identifier)
? this.#byDid.get(identifier)
: this.#byHandle.get(identifier);
if (hit) return Promise.resolve(hit);
const existing = this.#inflight.get(identifier);
if (existing) return existing;
const pending = resolveMiniDoc(this.#ctx, identifier, init)
.then((doc) => {
this.prime(doc);
return doc;
})
.finally(() => this.#inflight.delete(identifier));
this.#inflight.set(identifier, pending);
return pending;
}
}