add code for fetching single document

nulfrost 72ab5ac8 749d0494

+120
src/leaflet-live-loader.ts
··· 1 + import type { LiveLoader } from "astro/loaders"; 2 + import { Agent } from "@atproto/api"; 3 + import { isDid } from "@atproto/did"; 4 + import { isValidHandle } from "@atproto/syntax"; 5 + import { 6 + getLeafletDocuments, 7 + getSingleLeafletDocument, 8 + resolveMiniDoc, 9 + uriToRkey, 10 + } from "./utils.js"; 11 + import type { 12 + CollectionFilter, 13 + EntryFilter, 14 + LeafletRecord, 15 + LiveLoaderOptions, 16 + } from "./types.js"; 17 + 18 + export class LiveLoaderError extends Error { 19 + constructor( 20 + message: string, 21 + public code?: string, 22 + ) { 23 + super(message); 24 + this.name = "LiveLoaderError"; 25 + } 26 + } 27 + 28 + /** 29 + * Flow: 30 + * - Check for valid handle or did [done] 31 + * - Resolve PDS url from handle or did [done, thanks Phil!] 32 + * - Fetch leaflet documents [done] 33 + * - Find out how to use leaflet types here 34 + */ 35 + 36 + export function leafletLiveLoader( 37 + options: LiveLoaderOptions, 38 + ): LiveLoader<LeafletRecord, EntryFilter, CollectionFilter, LiveLoaderError> { 39 + const { repo } = options; 40 + 41 + if (!repo || typeof repo !== "string") { 42 + throw new LiveLoaderError( 43 + "missing or invalid handle or did", 44 + "MISSING_OR_INVALID_HANDLE_OR_DID", 45 + ); 46 + } 47 + 48 + if (!isValidHandle(repo)) { 49 + // not a valid handle, let's check if it's a valid did 50 + if (!isDid(repo)) { 51 + throw new LiveLoaderError( 52 + "invalid handle or did", 53 + "INVALID_HANDLE_OR_DID", 54 + ); 55 + } 56 + } 57 + 58 + return { 59 + name: "leaflet-live-loader", 60 + loadCollection: async ({ filter }) => { 61 + try { 62 + const pds_url = await resolveMiniDoc(repo); 63 + const agent = new Agent({ service: pds_url }); 64 + 65 + const documents = await getLeafletDocuments({ 66 + repo, 67 + agent, 68 + cursor: filter?.cursor, 69 + limit: filter?.limit, 70 + reverse: filter?.reverse, 71 + }); 72 + 73 + return { 74 + entries: documents.map((document) => ({ 75 + id: uriToRkey(document.uri), 76 + data: document, 77 + })), 78 + }; 79 + } catch (error) { 80 + return { 81 + error: new LiveLoaderError( 82 + "could not recover from error, please report on github", 83 + "UNRECOVERABLE_ERROR", 84 + ), 85 + }; 86 + } 87 + }, 88 + loadEntry: async ({ filter }) => { 89 + try { 90 + if (!filter.id) { 91 + return { 92 + error: new LiveLoaderError( 93 + "must provide an id for specific document", 94 + "MISSING_DOCUMENT_ID", 95 + ), 96 + }; 97 + } 98 + const pds_url = await resolveMiniDoc(repo); 99 + const agent = new Agent({ service: pds_url }); 100 + const document = await getSingleLeafletDocument({ 101 + agent, 102 + id: filter.id, 103 + repo, 104 + }); 105 + 106 + return { 107 + id: uriToRkey(document.data.uri), 108 + data: document.data.value, 109 + }; 110 + } catch { 111 + return { 112 + error: new LiveLoaderError( 113 + "could not recover from error, please report on github", 114 + "UNRECOVERABLE_ERROR", 115 + ), 116 + }; 117 + } 118 + }, 119 + }; 120 + }
-69
src/loader.ts
··· 1 - import type { LiveLoader } from "astro/loaders"; 2 - import { Agent } from "@atproto/api"; 3 - import { isDid } from "@atproto/did"; 4 - import { isValidHandle } from "@atproto/syntax"; 5 - import { getLeafletDocuments, resolveMiniDoc, uriToRkey } from "./utils.js"; 6 - import type { LeafletRecord, LiveLoaderOptions } from "./types.js"; 7 - 8 - export class LiveLoaderError extends Error { 9 - constructor(message: string, reason: string) { 10 - super(message); 11 - this.name = "LiveLoaderError"; 12 - } 13 - } 14 - 15 - /** 16 - * Flow: 17 - * - Check for valid handle or did [done] 18 - * - Resolve PDS url from handle or did [done, thanks Phil!] 19 - * - Fetch leaflet documents [done] 20 - */ 21 - 22 - export function leafletLiveLoader( 23 - options: LiveLoaderOptions, 24 - ): LiveLoader<LeafletRecord> { 25 - const { repo } = options; 26 - 27 - return { 28 - name: "leaflet-live-loader", 29 - loadCollection: async ({ filter }) => { 30 - if (!repo || typeof repo !== "string") { 31 - throw new LiveLoaderError( 32 - "missing or invalid handle or did", 33 - "MISSING_OR_INVALID_IDENTIFIER", 34 - ); 35 - } 36 - 37 - if (!isValidHandle(repo) || !isDid(repo)) { 38 - throw new LiveLoaderError( 39 - "invalid handle or did", 40 - "INVALID_IDENTIFIER", 41 - ); 42 - } 43 - 44 - // we know for sure the handle or did is valid now 45 - 46 - try { 47 - const pds_url = await resolveMiniDoc(repo); 48 - const agent = new Agent({ service: pds_url }); 49 - 50 - const response = await getLeafletDocuments(repo, agent); 51 - 52 - return { 53 - entries: response.data.records.map((document) => ({ 54 - id: uriToRkey(document.uri), 55 - data: document, 56 - })), 57 - }; 58 - } catch (error) { 59 - return { 60 - error: new LiveLoaderError( 61 - "Could not recover from error, please report on github", 62 - "UNRECOVERABLE_ERROR", 63 - ), 64 - }; 65 - } 66 - }, 67 - loadEntry: async () => {}, 68 - }; 69 - }
+26
src/types.ts
··· 1 + import type { Agent } from "@atproto/api"; 2 + 1 3 export interface LiveLoaderOptions { 2 4 /** 3 5 * @description Your repo is either your handle (@you.some.url) or your DID (did:plc... or did:web...). You can find this information using: https://pdsls.dev ··· 18 20 pds: string; 19 21 signing_key: string; 20 22 } 23 + 24 + export interface CollectionFilter { 25 + limit?: number; 26 + reverse?: boolean; 27 + cursor?: string; 28 + } 29 + 30 + export interface EntryFilter { 31 + id?: string; 32 + } 33 + 34 + export interface GetLeafletDocumentsParams { 35 + repo: string; 36 + agent: Agent; 37 + cursor?: string; 38 + limit?: number; 39 + reverse?: boolean; 40 + } 41 + 42 + export interface GetSingleLeafletDocumentParams { 43 + repo: string; 44 + agent: Agent; 45 + id: string; 46 + }
+37 -3
src/utils.ts
··· 1 1 import type { Agent } from "@atproto/api"; 2 - import type { MiniDoc } from "./types.js"; 3 - import { LiveLoaderError } from "./loader.js"; 2 + import type { 3 + GetLeafletDocumentsParams, 4 + GetSingleLeafletDocumentParams, 5 + MiniDoc, 6 + } from "./types.js"; 7 + import { LiveLoaderError } from "./leaflet-live-loader.js"; 4 8 5 9 export function uriToRkey(uri: string) { 6 10 const rkey = uri.split("/").pop(); ··· 29 33 } 30 34 } 31 35 32 - export async function getLeafletDocuments(repo: string, agent: Agent) { 36 + export async function getLeafletDocuments({ 37 + repo, 38 + reverse, 39 + cursor, 40 + agent, 41 + limit, 42 + }: GetLeafletDocumentsParams) { 33 43 const response = await agent.com.atproto.repo.listRecords({ 34 44 repo, 35 45 collection: "pub.leaflet.document", 46 + cursor, 47 + reverse, 48 + limit, 36 49 }); 37 50 38 51 if (response.success === false) { 39 52 throw new LiveLoaderError( 40 53 "Could not fetch leaflet documents", 41 54 "FETCH_FAILED", 55 + ); 56 + } 57 + 58 + return response?.data?.records; 59 + } 60 + 61 + export async function getSingleLeafletDocument({ 62 + agent, 63 + repo, 64 + id, 65 + }: GetSingleLeafletDocumentParams) { 66 + const response = await agent.com.atproto.repo.getRecord({ 67 + repo, 68 + collection: "pub.leaflet.document", 69 + rkey: id, 70 + }); 71 + 72 + if (response.success === false) { 73 + throw new LiveLoaderError( 74 + "error fetching document", 75 + "DOCUMENT_FETCH_ERROR", 42 76 ); 43 77 } 44 78