-181
src/test.ts
-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
-
}