~james/core

core/web/src/lib/api/identity.ts -rw-r--r-- 2.3 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 15 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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;
	}
}