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