+120
src/leaflet-live-loader.ts
+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
-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
+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
+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