search for standard sites pub-search.waow.tech
search zig blog atproto
0

Configure Feed

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

pub-search / site / identity.js
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})();