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