| 1 | /** |
| 2 | * Constructs a full URL to fetch a blob from a PDS. |
| 3 | * Format: {pds}/xrpc/com.atproto.sync.getBlob?did={did}&cid={cid} |
| 4 | */ |
| 5 | export function buildBlobUrl(pds: string, did: string, cid: string): string { |
| 6 | const baseUrl = pds.endsWith("/") ? pds.slice(0, -1) : pds; |
| 7 | return `${baseUrl}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`; |
| 8 | } |
| 9 | |
| 10 | /** |
| 11 | * Extracts the CID from a blob reference object. |
| 12 | * Blob refs can be in different formats: |
| 13 | * - { $link: "cid" } (legacy) |
| 14 | * - { ref: { $link: "cid" } } (current) |
| 15 | * - { cid: "cid" } (simple) |
| 16 | */ |
| 17 | export function extractBlobCid(blob: unknown): string | null { |
| 18 | if (!blob || typeof blob !== "object") return null; |
| 19 | |
| 20 | const b = blob as Record<string, unknown>; |
| 21 | |
| 22 | // Current format: { ref: { $link: "cid" } } |
| 23 | if (b.ref && typeof b.ref === "object") { |
| 24 | const ref = b.ref as Record<string, unknown>; |
| 25 | if (typeof ref.$link === "string") return ref.$link; |
| 26 | } |
| 27 | |
| 28 | // Legacy format: { $link: "cid" } |
| 29 | if (typeof b.$link === "string") return b.$link; |
| 30 | |
| 31 | // Simple format: { cid: "cid" } |
| 32 | if (typeof b.cid === "string") return b.cid; |
| 33 | |
| 34 | return null; |
| 35 | } |