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