this repo has no description
1const API_BASE = '/xrpc'
2
3export class ApiError extends Error {
4 public did?: string
5 constructor(public status: number, public error: string, message: string, did?: string) {
6 super(message)
7 this.name = 'ApiError'
8 this.did = did
9 }
10}
11
12async function xrpc<T>(method: string, options?: {
13 method?: 'GET' | 'POST'
14 params?: Record<string, string>
15 body?: unknown
16 token?: string
17}): Promise<T> {
18 const { method: httpMethod = 'GET', params, body, token } = options ?? {}
19 let url = `${API_BASE}/${method}`
20 if (params) {
21 const searchParams = new URLSearchParams(params)
22 url += `?${searchParams}`
23 }
24 const headers: Record<string, string> = {}
25 if (token) {
26 headers['Authorization'] = `Bearer ${token}`
27 }
28 if (body) {
29 headers['Content-Type'] = 'application/json'
30 }
31 const res = await fetch(url, {
32 method: httpMethod,
33 headers,
34 body: body ? JSON.stringify(body) : undefined,
35 })
36 if (!res.ok) {
37 const err = await res.json().catch(() => ({ error: 'Unknown', message: res.statusText }))
38 throw new ApiError(res.status, err.error, err.message, err.did)
39 }
40 return res.json()
41}
42
43export interface Session {
44 did: string
45 handle: string
46 email?: string
47 emailConfirmed?: boolean
48 preferredChannel?: string
49 preferredChannelVerified?: boolean
50 accessJwt: string
51 refreshJwt: string
52}
53
54export interface AppPassword {
55 name: string
56 createdAt: string
57}
58
59export interface InviteCode {
60 code: string
61 available: number
62 disabled: boolean
63 forAccount: string
64 createdBy: string
65 createdAt: string
66 uses: { usedBy: string; usedAt: string }[]
67}
68
69export type VerificationChannel = 'email' | 'discord' | 'telegram' | 'signal'
70
71export interface CreateAccountParams {
72 handle: string
73 email: string
74 password: string
75 inviteCode?: string
76 verificationChannel?: VerificationChannel
77 discordId?: string
78 telegramUsername?: string
79 signalNumber?: string
80}
81
82export interface CreateAccountResult {
83 handle: string
84 did: string
85 verificationRequired: boolean
86 verificationChannel: string
87}
88
89export interface ConfirmSignupResult {
90 accessJwt: string
91 refreshJwt: string
92 handle: string
93 did: string
94 email?: string
95 emailConfirmed?: boolean
96 preferredChannel?: string
97 preferredChannelVerified?: boolean
98}
99
100export const api = {
101 async createAccount(params: CreateAccountParams): Promise<CreateAccountResult> {
102 return xrpc('com.atproto.server.createAccount', {
103 method: 'POST',
104 body: {
105 handle: params.handle,
106 email: params.email,
107 password: params.password,
108 inviteCode: params.inviteCode,
109 verificationChannel: params.verificationChannel,
110 discordId: params.discordId,
111 telegramUsername: params.telegramUsername,
112 signalNumber: params.signalNumber,
113 },
114 })
115 },
116
117 async confirmSignup(did: string, verificationCode: string): Promise<ConfirmSignupResult> {
118 return xrpc('com.atproto.server.confirmSignup', {
119 method: 'POST',
120 body: { did, verificationCode },
121 })
122 },
123
124 async resendVerification(did: string): Promise<{ success: boolean }> {
125 return xrpc('com.atproto.server.resendVerification', {
126 method: 'POST',
127 body: { did },
128 })
129 },
130
131 async createSession(identifier: string, password: string): Promise<Session> {
132 return xrpc('com.atproto.server.createSession', {
133 method: 'POST',
134 body: { identifier, password },
135 })
136 },
137
138 async getSession(token: string): Promise<Session> {
139 return xrpc('com.atproto.server.getSession', { token })
140 },
141
142 async refreshSession(refreshJwt: string): Promise<Session> {
143 return xrpc('com.atproto.server.refreshSession', {
144 method: 'POST',
145 token: refreshJwt,
146 })
147 },
148
149 async deleteSession(token: string): Promise<void> {
150 await xrpc('com.atproto.server.deleteSession', {
151 method: 'POST',
152 token,
153 })
154 },
155
156 async listAppPasswords(token: string): Promise<{ passwords: AppPassword[] }> {
157 return xrpc('com.atproto.server.listAppPasswords', { token })
158 },
159
160 async createAppPassword(token: string, name: string): Promise<{ name: string; password: string; createdAt: string }> {
161 return xrpc('com.atproto.server.createAppPassword', {
162 method: 'POST',
163 token,
164 body: { name },
165 })
166 },
167
168 async revokeAppPassword(token: string, name: string): Promise<void> {
169 await xrpc('com.atproto.server.revokeAppPassword', {
170 method: 'POST',
171 token,
172 body: { name },
173 })
174 },
175
176 async getAccountInviteCodes(token: string): Promise<{ codes: InviteCode[] }> {
177 return xrpc('com.atproto.server.getAccountInviteCodes', { token })
178 },
179
180 async createInviteCode(token: string, useCount: number = 1): Promise<{ code: string }> {
181 return xrpc('com.atproto.server.createInviteCode', {
182 method: 'POST',
183 token,
184 body: { useCount },
185 })
186 },
187
188 async requestPasswordReset(email: string): Promise<void> {
189 await xrpc('com.atproto.server.requestPasswordReset', {
190 method: 'POST',
191 body: { email },
192 })
193 },
194
195 async resetPassword(token: string, password: string): Promise<void> {
196 await xrpc('com.atproto.server.resetPassword', {
197 method: 'POST',
198 body: { token, password },
199 })
200 },
201
202 async requestEmailUpdate(token: string): Promise<{ tokenRequired: boolean }> {
203 return xrpc('com.atproto.server.requestEmailUpdate', {
204 method: 'POST',
205 token,
206 })
207 },
208
209 async updateEmail(token: string, email: string, emailToken?: string): Promise<void> {
210 await xrpc('com.atproto.server.updateEmail', {
211 method: 'POST',
212 token,
213 body: { email, token: emailToken },
214 })
215 },
216
217 async updateHandle(token: string, handle: string): Promise<void> {
218 await xrpc('com.atproto.identity.updateHandle', {
219 method: 'POST',
220 token,
221 body: { handle },
222 })
223 },
224
225 async requestAccountDelete(token: string): Promise<void> {
226 await xrpc('com.atproto.server.requestAccountDelete', {
227 method: 'POST',
228 token,
229 })
230 },
231
232 async deleteAccount(did: string, password: string, deleteToken: string): Promise<void> {
233 await xrpc('com.atproto.server.deleteAccount', {
234 method: 'POST',
235 body: { did, password, token: deleteToken },
236 })
237 },
238
239 async describeServer(): Promise<{
240 availableUserDomains: string[]
241 inviteCodeRequired: boolean
242 links?: { privacyPolicy?: string; termsOfService?: string }
243 }> {
244 return xrpc('com.atproto.server.describeServer')
245 },
246
247 async getNotificationPrefs(token: string): Promise<{
248 preferredChannel: string
249 email: string
250 discordId: string | null
251 discordVerified: boolean
252 telegramUsername: string | null
253 telegramVerified: boolean
254 signalNumber: string | null
255 signalVerified: boolean
256 }> {
257 return xrpc('com.bspds.account.getNotificationPrefs', { token })
258 },
259
260 async updateNotificationPrefs(token: string, prefs: {
261 preferredChannel?: string
262 discordId?: string
263 telegramUsername?: string
264 signalNumber?: string
265 }): Promise<{ success: boolean }> {
266 return xrpc('com.bspds.account.updateNotificationPrefs', {
267 method: 'POST',
268 token,
269 body: prefs,
270 })
271 },
272
273 async describeRepo(token: string, repo: string): Promise<{
274 handle: string
275 did: string
276 didDoc: unknown
277 collections: string[]
278 handleIsCorrect: boolean
279 }> {
280 return xrpc('com.atproto.repo.describeRepo', {
281 token,
282 params: { repo },
283 })
284 },
285
286 async listRecords(token: string, repo: string, collection: string, options?: {
287 limit?: number
288 cursor?: string
289 reverse?: boolean
290 }): Promise<{
291 records: Array<{ uri: string; cid: string; value: unknown }>
292 cursor?: string
293 }> {
294 const params: Record<string, string> = { repo, collection }
295 if (options?.limit) params.limit = String(options.limit)
296 if (options?.cursor) params.cursor = options.cursor
297 if (options?.reverse) params.reverse = 'true'
298 return xrpc('com.atproto.repo.listRecords', { token, params })
299 },
300
301 async getRecord(token: string, repo: string, collection: string, rkey: string): Promise<{
302 uri: string
303 cid: string
304 value: unknown
305 }> {
306 return xrpc('com.atproto.repo.getRecord', {
307 token,
308 params: { repo, collection, rkey },
309 })
310 },
311
312 async createRecord(token: string, repo: string, collection: string, record: unknown, rkey?: string): Promise<{
313 uri: string
314 cid: string
315 }> {
316 return xrpc('com.atproto.repo.createRecord', {
317 method: 'POST',
318 token,
319 body: { repo, collection, record, rkey },
320 })
321 },
322
323 async putRecord(token: string, repo: string, collection: string, rkey: string, record: unknown): Promise<{
324 uri: string
325 cid: string
326 }> {
327 return xrpc('com.atproto.repo.putRecord', {
328 method: 'POST',
329 token,
330 body: { repo, collection, rkey, record },
331 })
332 },
333
334 async deleteRecord(token: string, repo: string, collection: string, rkey: string): Promise<void> {
335 await xrpc('com.atproto.repo.deleteRecord', {
336 method: 'POST',
337 token,
338 body: { repo, collection, rkey },
339 })
340 },
341}