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