fix type errors for loaders

nulfrost a1e85579 72ab5ac8

+20 -11
src/leaflet-live-loader.ts
··· 12 12 CollectionFilter, 13 13 EntryFilter, 14 14 LeafletRecord, 15 - LiveLoaderOptions, 15 + LeafletLoaderOptions, 16 16 } from "./types.js"; 17 17 18 18 export class LiveLoaderError extends Error { ··· 34 34 */ 35 35 36 36 export function leafletLiveLoader( 37 - options: LiveLoaderOptions, 37 + options: LeafletLoaderOptions, 38 38 ): LiveLoader<LeafletRecord, EntryFilter, CollectionFilter, LiveLoaderError> { 39 39 const { repo } = options; 40 40 ··· 56 56 } 57 57 58 58 return { 59 - name: "leaflet-live-loader", 59 + name: "leaflet-loader-astro", 60 60 loadCollection: async ({ filter }) => { 61 61 try { 62 62 const pds_url = await resolveMiniDoc(repo); 63 63 const agent = new Agent({ service: pds_url }); 64 64 65 65 const documents = await getLeafletDocuments({ 66 - repo, 67 66 agent, 67 + repo, 68 + reverse: filter?.reverse, 68 69 cursor: filter?.cursor, 69 70 limit: filter?.limit, 70 - reverse: filter?.reverse, 71 71 }); 72 72 73 73 return { 74 - entries: documents.map((document) => ({ 75 - id: uriToRkey(document.uri), 76 - data: document, 77 - })), 74 + entries: documents.map((document) => { 75 + const id = uriToRkey(document.uri); 76 + return { 77 + id, 78 + data: { 79 + id, 80 + ...document, 81 + }, 82 + }; 83 + }), 78 84 }; 79 85 } catch (error) { 80 86 return { ··· 104 110 }); 105 111 106 112 return { 107 - id: uriToRkey(document.data.uri), 108 - data: document.data.value, 113 + id: filter?.id, 114 + data: { 115 + id: filter?.id, 116 + ...document, 117 + }, 109 118 }; 110 119 } catch { 111 120 return {
+181
src/test.ts
··· 1 + import { 2 + AtpAgent, 3 + type AppBskyFeedGetAuthorFeed, 4 + type AppBskyFeedDefs, 5 + } from "@atproto/api"; 6 + import type { LiveLoader } from "astro/loaders"; 7 + 8 + export interface LiveBlueskyLoaderOptions { 9 + identifier?: string; 10 + service?: string; 11 + } 12 + 13 + export interface CollectionFilter { 14 + limit?: number; 15 + since?: Date; 16 + until?: Date; 17 + type?: AppBskyFeedGetAuthorFeed.QueryParams["filter"]; 18 + identifier?: string; 19 + } 20 + 21 + export interface EntryFilter { 22 + id?: string; 23 + } 24 + 25 + export class BlueskyError extends Error { 26 + constructor( 27 + message: string, 28 + public code?: string, 29 + public identifier?: string, 30 + ) { 31 + super(message); 32 + this.name = "BlueskyError"; 33 + } 34 + } 35 + 36 + export function liveBlueskyLoader( 37 + options: LiveBlueskyLoaderOptions = {}, 38 + ): LiveLoader< 39 + AppBskyFeedDefs.PostView, 40 + EntryFilter, 41 + CollectionFilter, 42 + BlueskyError 43 + > { 44 + const { 45 + identifier: defaultIdentifier, 46 + service = "https://public.api.bsky.app", 47 + } = options; 48 + 49 + return { 50 + name: "live-bluesky-loader", 51 + 52 + loadCollection: async ({ filter }) => { 53 + try { 54 + const identifier = filter?.identifier || defaultIdentifier; 55 + 56 + if (!identifier) { 57 + return { 58 + error: new BlueskyError( 59 + "Identifier must be provided either in loader options or collection filter", 60 + "MISSING_IDENTIFIER", 61 + ), 62 + }; 63 + } 64 + 65 + const agent = new AtpAgent({ service }); 66 + 67 + let cursor = undefined; 68 + const allPosts: AppBskyFeedDefs.PostView[] = []; 69 + let count = 0; 70 + 71 + do { 72 + const { data } = await agent.getAuthorFeed({ 73 + actor: identifier, 74 + filter: filter?.type, 75 + cursor, 76 + limit: 100, 77 + }); 78 + 79 + for (const { post } of data.feed) { 80 + // Apply collection filters 81 + if (filter?.limit && count >= filter.limit) { 82 + break; 83 + } 84 + 85 + if (filter?.since) { 86 + const postDate = new Date(post.indexedAt); 87 + if (postDate < filter.since) { 88 + continue; 89 + } 90 + } 91 + 92 + if (filter?.until) { 93 + const postDate = new Date(post.indexedAt); 94 + if (postDate > filter.until) { 95 + continue; 96 + } 97 + } 98 + 99 + allPosts.push(post); 100 + count++; 101 + } 102 + 103 + cursor = data.cursor; 104 + } while (cursor && (!filter?.limit || count < filter.limit)); 105 + 106 + return { 107 + entries: allPosts.map((post) => ({ 108 + id: post.uri, 109 + data: post, 110 + // rendered: { 111 + // html: renderPostAsHtml(post), 112 + // }, 113 + })), 114 + }; 115 + } catch (error) { 116 + const identifier = filter?.identifier || defaultIdentifier; 117 + return { 118 + error: new BlueskyError( 119 + `Failed to load Bluesky posts for ${identifier || "unknown"}`, 120 + "COLLECTION_LOAD_ERROR", 121 + identifier, 122 + ), 123 + }; 124 + } 125 + }, 126 + 127 + loadEntry: async ({ filter }) => { 128 + try { 129 + const agent = new AtpAgent({ service }); 130 + 131 + if (!filter.id) { 132 + return { 133 + error: new BlueskyError( 134 + "'id' must be provided in the filter", 135 + "INVALID_FILTER", 136 + ), 137 + }; 138 + } 139 + 140 + // Validate that the ID is a full AT URI 141 + if (!filter.id.startsWith("at://")) { 142 + return { 143 + error: new BlueskyError( 144 + `Invalid ID format: '${filter.id}'. Must be a full AT URI (e.g., 'at://did:plc:user/app.bsky.feed.post/id')`, 145 + "INVALID_ID_FORMAT", 146 + ), 147 + }; 148 + } 149 + 150 + const postUri = filter.id; 151 + 152 + // Fetch the post directly using getPosts 153 + const { data } = await agent.getPosts({ uris: [postUri] }); 154 + 155 + const [post] = data.posts; 156 + 157 + if (!post) { 158 + return; 159 + } 160 + 161 + return { 162 + id: post.uri, 163 + data: post, 164 + // rendered: { 165 + // html: renderPostAsHtml(post), 166 + // }, 167 + }; 168 + } catch (error) { 169 + const errorMessage = 170 + error instanceof Error ? error.message : "Unknown error"; 171 + const requestedUri = filter.id || "unknown"; 172 + return { 173 + error: new BlueskyError( 174 + `Failed to load Bluesky post '${requestedUri}': ${errorMessage}`, 175 + "ENTRY_LOAD_ERROR", 176 + ), 177 + }; 178 + } 179 + }, 180 + }; 181 + }
+2 -2
src/types.ts
··· 1 1 import type { Agent } from "@atproto/api"; 2 2 3 - export interface LiveLoaderOptions { 3 + export interface LeafletLoaderOptions { 4 4 /** 5 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 6 6 */ ··· 10 10 export interface LeafletRecord { 11 11 id: string; 12 12 uri: string; 13 - cid: string; 13 + cid?: string; 14 14 value: unknown; 15 15 } 16 16
+2 -2
src/utils.ts
··· 6 6 } from "./types.js"; 7 7 import { LiveLoaderError } from "./leaflet-live-loader.js"; 8 8 9 - export function uriToRkey(uri: string) { 9 + export function uriToRkey(uri: string): string { 10 10 const rkey = uri.split("/").pop(); 11 11 if (!rkey) { 12 12 throw new Error("Failed to get rkey from uri."); ··· 76 76 ); 77 77 } 78 78 79 - return response; 79 + return response?.data; 80 80 }