1import type { LiveLoader } from "astro/loaders"; 2import { Agent } from "@atproto/api"; 3import { isDid } from "@atproto/did"; 4import { isValidHandle } from "@atproto/syntax"; 5import { 6 getLeafletDocuments, 7 getSingleLeafletDocument, 8 leafletDocumentRecordToView, 9 resolveMiniDoc, 10 uriToRkey, 11} from "./utils.js"; 12import type { 13 CollectionFilter, 14 EntryFilter, 15 LeafletDocumentRecord, 16 LeafletDocumentView, 17 LeafletLoaderOptions, 18} from "./types.js"; 19 20export class LiveLoaderError extends Error { 21 constructor( 22 message: string, 23 public code?: string, 24 ) { 25 super(message); 26 this.name = "LiveLoaderError"; 27 } 28} 29 30/** 31 * Flow: 32 * - Check for valid handle or did [done] 33 * - Resolve PDS url from handle or did [done, thanks Phil!] 34 * - Fetch leaflet documents [done] 35 * - Find out how to use leaflet types here 36 */ 37 38export function leafletLiveLoader( 39 options: LeafletLoaderOptions, 40): LiveLoader< 41 LeafletDocumentView, 42 EntryFilter, 43 CollectionFilter, 44 LiveLoaderError 45> { 46 const { repo } = options; 47 48 if (!repo || typeof repo !== "string") { 49 throw new LiveLoaderError( 50 "missing or invalid handle or did", 51 "MISSING_OR_INVALID_HANDLE_OR_DID", 52 ); 53 } 54 55 if (!isValidHandle(repo)) { 56 // not a valid handle, let's check if it's a valid did 57 if (!isDid(repo)) { 58 throw new LiveLoaderError( 59 "invalid handle or did", 60 "INVALID_HANDLE_OR_DID", 61 ); 62 } 63 } 64 65 return { 66 name: "leaflet-loader-astro", 67 loadCollection: async ({ filter }) => { 68 try { 69 const pds_url = await resolveMiniDoc(repo); 70 const agent = new Agent({ service: pds_url }); 71 72 const documents = await getLeafletDocuments({ 73 agent, 74 repo, 75 reverse: filter?.reverse, 76 cursor: filter?.cursor, 77 limit: filter?.limit, 78 }); 79 80 return { 81 entries: documents.map((document) => { 82 const id = uriToRkey(document.uri); 83 return { 84 id, 85 data: leafletDocumentRecordToView({ 86 uri: document.uri, 87 cid: document.cid, 88 value: document.value as unknown as LeafletDocumentRecord, 89 }), 90 }; 91 }), 92 }; 93 } catch (error) { 94 return { 95 error: new LiveLoaderError( 96 "could not recover from error, please report on github", 97 "UNRECOVERABLE_ERROR", 98 ), 99 }; 100 } 101 }, 102 loadEntry: async ({ filter }) => { 103 try { 104 if (!filter.id) { 105 return { 106 error: new LiveLoaderError( 107 "must provide an id for specific document", 108 "MISSING_DOCUMENT_ID", 109 ), 110 }; 111 } 112 const pds_url = await resolveMiniDoc(repo); 113 const agent = new Agent({ service: pds_url }); 114 const document = await getSingleLeafletDocument({ 115 agent, 116 id: filter.id, 117 repo, 118 }); 119 120 return { 121 id: filter.id, 122 data: leafletDocumentRecordToView({ 123 uri: document.uri, 124 cid: document.cid?.toString() ?? "", 125 value: document.value as unknown as LeafletDocumentRecord, 126 }), 127 }; 128 } catch { 129 return { 130 error: new LiveLoaderError( 131 "could not recover from error, please report on github", 132 "UNRECOVERABLE_ERROR", 133 ), 134 }; 135 } 136 }, 137 }; 138}