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