search for standard sites
pub-search.waow.tech
search
zig
blog
atproto
3.0 kB
77 lines
1// LeafletIdentity — batch DID/handle → profile resolution against typeahead,
2// the community-run drop-in replacement for app.bsky.actor.getProfiles. We
3// route here instead of hitting bsky directly so we're not contributing to
4// public.api.bsky.app's load for what's effectively a cacheable identity
5// lookup, and so we stay inside the microcosm/atproto-native stack.
6//
7// Live: https://typeahead.waow.tech (source: ../typeahead in this org)
8//
9// Usage:
10// var ids = await window.LeafletIdentity.resolveProfiles([did1, did2, ...]);
11// ids.get(did1) // → { did, handle, displayName?, avatar?, ... } or undefined
12(function () {
13 var BASE = 'https://typeahead.waow.tech';
14 var MAX_PER_CALL = 25; // matches the upstream lexicon's cap
15
16 // Module-level memo so repeated lookups for the same actor across a page
17 // life don't refetch. Keyed by the input string (did or handle).
18 var cache = new Map();
19
20 function resolveProfiles(actors) {
21 if (!actors || actors.length === 0) return Promise.resolve(new Map());
22
23 var out = new Map();
24 var missing = [];
25 actors.forEach(function (a) {
26 if (!a) return;
27 if (cache.has(a)) {
28 var p = cache.get(a);
29 if (p) out.set(a, p);
30 } else {
31 missing.push(a);
32 }
33 });
34 if (missing.length === 0) return Promise.resolve(out);
35
36 // dedupe; bsky's getProfiles caps at 25/call
37 var unique = Array.from(new Set(missing));
38 var chunks = [];
39 for (var i = 0; i < unique.length; i += MAX_PER_CALL) {
40 chunks.push(unique.slice(i, i + MAX_PER_CALL));
41 }
42
43 return Promise.all(chunks.map(function (chunk) {
44 var qs = chunk.map(function (a) { return 'actors=' + encodeURIComponent(a); }).join('&');
45 return fetch(BASE + '/xrpc/app.bsky.actor.getProfiles?' + qs,
46 { headers: { 'X-Client': 'pub-search.waow.tech' } })
47 .then(function (r) { return r.ok ? r.json() : null; })
48 .catch(function () { return null; });
49 })).then(function (results) {
50 results.forEach(function (d) {
51 if (!d || !Array.isArray(d.profiles)) return;
52 d.profiles.forEach(function (p) {
53 if (!p) return;
54 // index by both DID and handle so callers using either form hit
55 // the cache on subsequent calls without another network round-trip.
56 if (p.did) cache.set(p.did, p);
57 if (p.handle) cache.set(p.handle, p);
58 if (p.did) out.set(p.did, p);
59 if (p.handle) out.set(p.handle, p);
60 });
61 });
62 // negative cache: actors we asked about but didn't get back stay
63 // out of cache so a later call can retry (handle resolution sometimes
64 // races with the ingester).
65 return out;
66 });
67 }
68
69 function resolveProfile(actor) {
70 return resolveProfiles([actor]).then(function (m) { return m.get(actor) || null; });
71 }
72
73 window.LeafletIdentity = {
74 resolveProfiles: resolveProfiles,
75 resolveProfile: resolveProfile,
76 };
77})();