This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / api / identity.ts
1.6 kB 60 lines
1import type { BobbinContext, XrpcRequestInit } from "./client"; 2import { jsonGet } from "./_request"; 3 4export interface MiniDoc { 5 did: string; 6 handle: string; 7 pds?: string; 8 avatar?: string; 9} 10 11export const resolveMiniDoc = ( 12 ctx: BobbinContext, 13 identifier: string, 14 init?: XrpcRequestInit 15): Promise<MiniDoc> => 16 jsonGet<MiniDoc>(ctx, "com.bad-example.identity.resolveMiniDoc", { identifier }, init); 17 18// did/handle cache with in-flight de-dupe. 19export class IdentityCache { 20 readonly #ctx: BobbinContext; 21 readonly #byDid = new Map<string, MiniDoc>(); 22 readonly #byHandle = new Map<string, MiniDoc>(); 23 readonly #inflight = new Map<string, Promise<MiniDoc>>(); 24 25 constructor(ctx: BobbinContext) { 26 this.#ctx = ctx; 27 } 28 29 prime(doc: MiniDoc): void { 30 this.#byDid.set(doc.did, doc); 31 this.#byHandle.set(doc.handle, doc); 32 } 33 34 didFor(handle: string): string | undefined { 35 return this.#byHandle.get(handle)?.did; 36 } 37 38 handleFor(did: string): string | undefined { 39 return this.#byDid.get(did)?.handle; 40 } 41 42 resolve(identifier: string, init?: XrpcRequestInit): Promise<MiniDoc> { 43 const cached = identifier.startsWith("did:") 44 ? this.#byDid.get(identifier) 45 : this.#byHandle.get(identifier); 46 if (cached) return Promise.resolve(cached); 47 48 const existing = this.#inflight.get(identifier); 49 if (existing) return existing; 50 51 const pending = resolveMiniDoc(this.#ctx, identifier, init) 52 .then((doc) => { 53 this.prime(doc); 54 return doc; 55 }) 56 .finally(() => this.#inflight.delete(identifier)); 57 this.#inflight.set(identifier, pending); 58 return pending; 59 } 60}