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 isAdmin?: boolean
51 accessJwt: string
52 refreshJwt: string
53}
54
55export interface AppPassword {
56 name: string
57 createdAt: string
58}
59
60export interface InviteCode {
61 code: string
62 available: number
63 disabled: boolean
64 forAccount: string
65 createdBy: string
66 createdAt: string
67 uses: { usedBy: string; usedAt: string }[]
68}
69
70export type VerificationChannel = 'email' | 'discord' | 'telegram' | 'signal'
71
72export interface CreateAccountParams {
73 handle: string
74 email: string
75 password: string
76 inviteCode?: string
77 verificationChannel?: VerificationChannel
78 discordId?: string
79 telegramUsername?: string
80 signalNumber?: string
81}
82
83export interface CreateAccountResult {
84 handle: string
85 did: string
86 verificationRequired: boolean
87 verificationChannel: string
88}
89
90export interface ConfirmSignupResult {
91 accessJwt: string
92 refreshJwt: string
93 handle: string
94 did: string
95 email?: string
96 emailConfirmed?: boolean
97 preferredChannel?: string
98 preferredChannelVerified?: boolean
99}
100
101export const api = {
102 async createAccount(params: CreateAccountParams): Promise<CreateAccountResult> {
103 return xrpc('com.atproto.server.createAccount', {
104 method: 'POST',
105 body: {
106 handle: params.handle,
107 email: params.email,
108 password: params.password,
109 inviteCode: params.inviteCode,
110 verificationChannel: params.verificationChannel,
111 discordId: params.discordId,
112 telegramUsername: params.telegramUsername,
113 signalNumber: params.signalNumber,
114 },
115 })
116 },
117
118 async confirmSignup(did: string, verificationCode: string): Promise<ConfirmSignupResult> {
119 return xrpc('com.atproto.server.confirmSignup', {
120 method: 'POST',
121 body: { did, verificationCode },
122 })
123 },
124
125 async resendVerification(did: string): Promise<{ success: boolean }> {
126 return xrpc('com.atproto.server.resendVerification', {
127 method: 'POST',
128 body: { did },
129 })
130 },
131
132 async createSession(identifier: string, password: string): Promise<Session> {
133 return xrpc('com.atproto.server.createSession', {
134 method: 'POST',
135 body: { identifier, password },
136 })
137 },
138
139 async getSession(token: string): Promise<Session> {
140 return xrpc('com.atproto.server.getSession', { token })
141 },
142
143 async refreshSession(refreshJwt: string): Promise<Session> {
144 return xrpc('com.atproto.server.refreshSession', {
145 method: 'POST',
146 token: refreshJwt,
147 })
148 },
149
150 async deleteSession(token: string): Promise<void> {
151 await xrpc('com.atproto.server.deleteSession', {
152 method: 'POST',
153 token,
154 })
155 },
156
157 async listAppPasswords(token: string): Promise<{ passwords: AppPassword[] }> {
158 return xrpc('com.atproto.server.listAppPasswords', { token })
159 },
160
161 async createAppPassword(token: string, name: string): Promise<{ name: string; password: string; createdAt: string }> {
162 return xrpc('com.atproto.server.createAppPassword', {
163 method: 'POST',
164 token,
165 body: { name },
166 })
167 },
168
169 async revokeAppPassword(token: string, name: string): Promise<void> {
170 await xrpc('com.atproto.server.revokeAppPassword', {
171 method: 'POST',
172 token,
173 body: { name },
174 })
175 },
176
177 async getAccountInviteCodes(token: string): Promise<{ codes: InviteCode[] }> {
178 return xrpc('com.atproto.server.getAccountInviteCodes', { token })
179 },
180
181 async createInviteCode(token: string, useCount: number = 1): Promise<{ code: string }> {
182 return xrpc('com.atproto.server.createInviteCode', {
183 method: 'POST',
184 token,
185 body: { useCount },
186 })
187 },
188
189 async requestPasswordReset(email: string): Promise<void> {
190 await xrpc('com.atproto.server.requestPasswordReset', {
191 method: 'POST',
192 body: { email },
193 })
194 },
195
196 async resetPassword(token: string, password: string): Promise<void> {
197 await xrpc('com.atproto.server.resetPassword', {
198 method: 'POST',
199 body: { token, password },
200 })
201 },
202
203 async requestEmailUpdate(token: string): Promise<{ tokenRequired: boolean }> {
204 return xrpc('com.atproto.server.requestEmailUpdate', {
205 method: 'POST',
206 token,
207 })
208 },
209
210 async updateEmail(token: string, email: string, emailToken?: string): Promise<void> {
211 await xrpc('com.atproto.server.updateEmail', {
212 method: 'POST',
213 token,
214 body: { email, token: emailToken },
215 })
216 },
217
218 async updateHandle(token: string, handle: string): Promise<void> {
219 await xrpc('com.atproto.identity.updateHandle', {
220 method: 'POST',
221 token,
222 body: { handle },
223 })
224 },
225
226 async requestAccountDelete(token: string): Promise<void> {
227 await xrpc('com.atproto.server.requestAccountDelete', {
228 method: 'POST',
229 token,
230 })
231 },
232
233 async deleteAccount(did: string, password: string, deleteToken: string): Promise<void> {
234 await xrpc('com.atproto.server.deleteAccount', {
235 method: 'POST',
236 body: { did, password, token: deleteToken },
237 })
238 },
239
240 async describeServer(): Promise<{
241 availableUserDomains: string[]
242 inviteCodeRequired: boolean
243 links?: { privacyPolicy?: string; termsOfService?: string }
244 }> {
245 return xrpc('com.atproto.server.describeServer')
246 },
247
248 async getNotificationPrefs(token: string): Promise<{
249 preferredChannel: string
250 email: string
251 discordId: string | null
252 discordVerified: boolean
253 telegramUsername: string | null
254 telegramVerified: boolean
255 signalNumber: string | null
256 signalVerified: boolean
257 }> {
258 return xrpc('com.bspds.account.getNotificationPrefs', { token })
259 },
260
261 async updateNotificationPrefs(token: string, prefs: {
262 preferredChannel?: string
263 discordId?: string
264 telegramUsername?: string
265 signalNumber?: string
266 }): Promise<{ success: boolean }> {
267 return xrpc('com.bspds.account.updateNotificationPrefs', {
268 method: 'POST',
269 token,
270 body: prefs,
271 })
272 },
273
274 async confirmChannelVerification(token: string, channel: string, code: string): Promise<{ success: boolean }> {
275 return xrpc('com.bspds.account.confirmChannelVerification', {
276 method: 'POST',
277 token,
278 body: { channel, code },
279 })
280 },
281
282 async getNotificationHistory(token: string): Promise<{
283 notifications: Array<{
284 createdAt: string
285 channel: string
286 notificationType: string
287 status: string
288 subject: string | null
289 body: string
290 }>
291 }> {
292 return xrpc('com.bspds.account.getNotificationHistory', { token })
293 },
294
295 async getServerStats(token: string): Promise<{
296 userCount: number
297 repoCount: number
298 recordCount: number
299 blobStorageBytes: number
300 }> {
301 return xrpc('com.bspds.admin.getServerStats', { token })
302 },
303
304 async changePassword(token: string, currentPassword: string, newPassword: string): Promise<void> {
305 await xrpc('com.bspds.account.changePassword', {
306 method: 'POST',
307 token,
308 body: { currentPassword, newPassword },
309 })
310 },
311
312 async listSessions(token: string): Promise<{
313 sessions: Array<{
314 id: string
315 createdAt: string
316 expiresAt: string
317 isCurrent: boolean
318 }>
319 }> {
320 return xrpc('com.bspds.account.listSessions', { token })
321 },
322
323 async revokeSession(token: string, sessionId: string): Promise<void> {
324 await xrpc('com.bspds.account.revokeSession', {
325 method: 'POST',
326 token,
327 body: { sessionId },
328 })
329 },
330
331 async searchAccounts(token: string, options?: {
332 handle?: string
333 cursor?: string
334 limit?: number
335 }): Promise<{
336 cursor?: string
337 accounts: Array<{
338 did: string
339 handle: string
340 email?: string
341 indexedAt: string
342 emailConfirmedAt?: string
343 deactivatedAt?: string
344 }>
345 }> {
346 const params: Record<string, string> = {}
347 if (options?.handle) params.handle = options.handle
348 if (options?.cursor) params.cursor = options.cursor
349 if (options?.limit) params.limit = String(options.limit)
350 return xrpc('com.atproto.admin.searchAccounts', { token, params })
351 },
352
353 async getInviteCodes(token: string, options?: {
354 sort?: 'recent' | 'usage'
355 cursor?: string
356 limit?: number
357 }): Promise<{
358 cursor?: string
359 codes: Array<{
360 code: string
361 available: number
362 disabled: boolean
363 forAccount: string
364 createdBy: string
365 createdAt: string
366 uses: Array<{ usedBy: string; usedAt: string }>
367 }>
368 }> {
369 const params: Record<string, string> = {}
370 if (options?.sort) params.sort = options.sort
371 if (options?.cursor) params.cursor = options.cursor
372 if (options?.limit) params.limit = String(options.limit)
373 return xrpc('com.atproto.admin.getInviteCodes', { token, params })
374 },
375
376 async disableInviteCodes(token: string, codes?: string[], accounts?: string[]): Promise<void> {
377 await xrpc('com.atproto.admin.disableInviteCodes', {
378 method: 'POST',
379 token,
380 body: { codes, accounts },
381 })
382 },
383
384 async getAccountInfo(token: string, did: string): Promise<{
385 did: string
386 handle: string
387 email?: string
388 indexedAt: string
389 emailConfirmedAt?: string
390 invitesDisabled?: boolean
391 deactivatedAt?: string
392 }> {
393 return xrpc('com.atproto.admin.getAccountInfo', { token, params: { did } })
394 },
395
396 async disableAccountInvites(token: string, account: string): Promise<void> {
397 await xrpc('com.atproto.admin.disableAccountInvites', {
398 method: 'POST',
399 token,
400 body: { account },
401 })
402 },
403
404 async enableAccountInvites(token: string, account: string): Promise<void> {
405 await xrpc('com.atproto.admin.enableAccountInvites', {
406 method: 'POST',
407 token,
408 body: { account },
409 })
410 },
411
412 async adminDeleteAccount(token: string, did: string): Promise<void> {
413 await xrpc('com.atproto.admin.deleteAccount', {
414 method: 'POST',
415 token,
416 body: { did },
417 })
418 },
419
420 async describeRepo(token: string, repo: string): Promise<{
421 handle: string
422 did: string
423 didDoc: unknown
424 collections: string[]
425 handleIsCorrect: boolean
426 }> {
427 return xrpc('com.atproto.repo.describeRepo', {
428 token,
429 params: { repo },
430 })
431 },
432
433 async listRecords(token: string, repo: string, collection: string, options?: {
434 limit?: number
435 cursor?: string
436 reverse?: boolean
437 }): Promise<{
438 records: Array<{ uri: string; cid: string; value: unknown }>
439 cursor?: string
440 }> {
441 const params: Record<string, string> = { repo, collection }
442 if (options?.limit) params.limit = String(options.limit)
443 if (options?.cursor) params.cursor = options.cursor
444 if (options?.reverse) params.reverse = 'true'
445 return xrpc('com.atproto.repo.listRecords', { token, params })
446 },
447
448 async getRecord(token: string, repo: string, collection: string, rkey: string): Promise<{
449 uri: string
450 cid: string
451 value: unknown
452 }> {
453 return xrpc('com.atproto.repo.getRecord', {
454 token,
455 params: { repo, collection, rkey },
456 })
457 },
458
459 async createRecord(token: string, repo: string, collection: string, record: unknown, rkey?: string): Promise<{
460 uri: string
461 cid: string
462 }> {
463 return xrpc('com.atproto.repo.createRecord', {
464 method: 'POST',
465 token,
466 body: { repo, collection, record, rkey },
467 })
468 },
469
470 async putRecord(token: string, repo: string, collection: string, rkey: string, record: unknown): Promise<{
471 uri: string
472 cid: string
473 }> {
474 return xrpc('com.atproto.repo.putRecord', {
475 method: 'POST',
476 token,
477 body: { repo, collection, rkey, record },
478 })
479 },
480
481 async deleteRecord(token: string, repo: string, collection: string, rkey: string): Promise<void> {
482 await xrpc('com.atproto.repo.deleteRecord', {
483 method: 'POST',
484 token,
485 body: { repo, collection, rkey },
486 })
487 },
488}