leaflet.pub astro loader
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}