Something went wrong. Try again.
visualize bsky quote trends sites.wisp.place/zzstoatzz.io/quotree
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106import { pacedFetch, RateLimitError } from './requests.js';export { RateLimitError } from './requests.js';
const SLINGSHOT = 'https://slingshot.microcosm.blue';const CONSTELLATION = 'https://constellation.microcosm.blue';const CDN = 'https://cdn.bsky.app/img/feed_thumbnail/plain';const BSKY_APPVIEW = 'https://public.api.bsky.app';
/** * fetch bsky profile (handle + avatar) for a did. * returns null on failure. */export async function getProfile(did) { try { const res = await pacedFetch( `${BSKY_APPVIEW}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(did)}` ); if (!res.ok) return null; return res.json(); } catch (error) { if (error instanceof RateLimitError) throw error; return null; }}
export async function resolveHandle(handle) { const res = await pacedFetch( `${SLINGSHOT}/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}` ); if (!res.ok) throw new Error(`failed to resolve handle: ${handle}`); const data = await res.json(); return data.did;}
export async function resolveMiniDoc(identifier) { const res = await pacedFetch( `${SLINGSHOT}/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=${encodeURIComponent(identifier)}` ); if (!res.ok) return null; return res.json();}
export async function getRecord(atUri) { const res = await pacedFetch( `${SLINGSHOT}/xrpc/blue.microcosm.repo.getRecordByUri?at_uri=${encodeURIComponent(atUri)}` ); if (!res.ok) throw new Error(`failed to fetch record: ${atUri}`); return res.json();}
export async function* getBacklinkPages(subjectUri, source) { let cursor = undefined;
while (true) { const params = new URLSearchParams({ subject: subjectUri, source, limit: '100', }); if (cursor) params.set('cursor', cursor);
const res = await pacedFetch( `${CONSTELLATION}/xrpc/blue.microcosm.links.getBacklinks?${params}` ); if (!res.ok) throw new Error(`backlink lookup failed (${res.status}): ${subjectUri}`);
const data = await res.json(); yield data.records || [];
if (!data.cursor) break; if (data.cursor === cursor) throw new Error(`backlink cursor did not advance: ${subjectUri}`); cursor = data.cursor; }}
export async function getAllBacklinks(subjectUri, source) { const all = []; for await (const page of getBacklinkPages(subjectUri, source)) all.push(...page); return all;}
/** * build a CDN image URL from a blob ref */export function imageUrl(did, blobRef) { // blobRef can be { $link: "..." } (legacy) or { ref: { $link: "..." } } const cid = blobRef?.$link || blobRef?.ref?.$link; if (!cid) return null; return `${CDN}/${did}/${cid}@jpeg`;}
/** * extract images from a post record value */export function extractImages(did, value) { // direct image embed: embed.images[] const images = value?.embed?.images || [];
// recordWithMedia embed: embed.media.images[] const mediaImages = value?.embed?.media?.images || [];
return [...images, ...mediaImages] .map((img) => imageUrl(did, img.image?.ref || img.image)) .filter(Boolean);}