A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

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

at trunk 1.7 kB View raw
1/** 2 * Read a writer's `app.bsky.actor.profile` straight from their PDS (SP10, step G). 3 * 4 * The public author index borrows the writer's Bluesky identity (name, bio, avatar, cover) to 5 * decorate the page. Per Decision 0010 this comes from the PDS record directly — NOT the 6 * Bluesky appview — so the page has no third-party-service dependency. `getRecord` already 7 * guards the (DID-doc-derived) PDS host against SSRF and degrades to null on failure. 8 * 9 * `displayName` is plain text. `description` is plain text in the record, but the author 10 * page linkifies it at render via `detectBioSegments` (rich-text.ts), mirroring Bluesky. 11 * Neither is ever injected as raw HTML. (Decision 0015.) 12 */ 13import { getRecord } from './records'; 14import { buildGetBlobUrl, type BlobRefJson } from '../media/blob'; 15 16export interface ActorProfile { 17 displayName: string | null; 18 description: string | null; 19 avatar: string | null; 20 banner: string | null; 21} 22 23interface RawActorProfile { 24 displayName?: string; 25 description?: string; 26 avatar?: BlobRefJson; 27 banner?: BlobRefJson; 28} 29 30function blobUrl( pdsUrl: string, did: string, blob?: BlobRefJson ): string | null { 31 const cid = blob?.ref?.$link; 32 return cid ? buildGetBlobUrl( pdsUrl, did, cid ) : null; 33} 34 35export async function fetchActorProfile( pdsUrl: string, did: string ): Promise< ActorProfile > { 36 const record = await getRecord< RawActorProfile >( 37 pdsUrl, 38 did, 39 'app.bsky.actor.profile', 40 'self' 41 ); 42 const value = record?.value ?? {}; 43 return { 44 displayName: value.displayName?.trim() || null, 45 description: value.description?.trim() || null, 46 avatar: blobUrl( pdsUrl, did, value.avatar ), 47 banner: blobUrl( pdsUrl, did, value.banner ), 48 }; 49}