Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at main 100 lines 2.8 kB view raw
1import {type Did, isDid} from '@atproto/api' 2import {useQuery} from '@tanstack/react-query' 3 4import {getDidDocumentUrl} from '#/lib/atproto/did' 5import {readPlcDirectory} from '#/state/preferences/plc-directory' 6import {createPublicAgent} from '#/state/session/agent' 7import {STALE} from '.' 8import {LRU} from './direct-fetch-record' 9const RQKEY_ROOT = 'resolve-identity' 10export const RQKEY = (did: string) => [RQKEY_ROOT, did] 11 12// this isn't trusted... 13export type DidDocument = { 14 '@context'?: string[] 15 id?: string 16 alsoKnownAs?: string[] 17 verificationMethod?: VerificationMethod[] 18 service?: Service[] 19} 20 21export type VerificationMethod = { 22 id?: string 23 type?: string 24 controller?: string 25 publicKeyMultibase?: string 26} 27 28export type Service = { 29 id?: string 30 type?: string 31 serviceEndpoint?: string 32} 33 34const serviceCache = new LRU<string, DidDocument>() 35 36async function resolveDidDocumentUsingAppView(did: Did) { 37 const agent = createPublicAgent() 38 try { 39 const res = await agent.com.atproto.identity.resolveDid({did}) 40 return res.data.didDoc as DidDocument 41 } finally { 42 agent.dispose() 43 } 44} 45 46export async function resolveDidDocument(did: Did) { 47 const plcDirectory = readPlcDirectory() 48 const cacheKey = did.startsWith('did:plc:') ? `${plcDirectory}|${did}` : did 49 50 return await serviceCache.getOrTryInsertWith(cacheKey, async () => { 51 const docUrl = getDidDocumentUrl(did, plcDirectory) 52 if (!docUrl) { 53 throw new Error(`Unsupported DID method for ${did}`) 54 } 55 56 try { 57 const res = await fetch(docUrl, { 58 headers: { 59 accept: 'application/did+ld+json, application/json', 60 }, 61 }) 62 if (!res.ok) { 63 throw new Error(`Failed to resolve DID document for ${did}`) 64 } 65 66 return (await res.json()) as DidDocument 67 } catch (err) { 68 if (!did.startsWith('did:web:')) { 69 throw err 70 } 71 return await resolveDidDocumentUsingAppView(did) 72 } 73 }) 74} 75 76export function findService(doc: DidDocument, id: string, type?: string) { 77 // probably not defensive enough, but we don't have atproto/did as a dep... 78 if (!Array.isArray(doc?.service)) return 79 return doc.service.find( 80 s => s?.serviceEndpoint && s?.id === id && (!type || s?.type === type), 81 ) 82} 83 84export async function resolvePdsServiceUrl(did: Did) { 85 const doc = await resolveDidDocument(did) 86 return findService(doc, '#atproto_pds', 'AtprotoPersonalDataServer') 87 ?.serviceEndpoint 88} 89 90export function useDidDocument({did}: {did: string}) { 91 return useQuery<DidDocument | undefined>({ 92 staleTime: STALE.HOURS.ONE, 93 queryKey: RQKEY(did || ''), 94 async queryFn() { 95 if (!isDid(did)) return undefined 96 return await resolveDidDocument(did) 97 }, 98 enabled: isDid(did) && !(did.includes('#') || did.includes('?')), 99 }) 100}