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 accessJwt: string
55 refreshJwt: string
56}
57
58export interface AppPassword {
59 name: string
60 createdAt: string
61}
62
63export interface InviteCode {
64 code: string
65 available: number
66 disabled: boolean
67 forAccount: string
68 createdBy: string
69 createdAt: string
70 uses: { usedBy: string; usedAt: string }[]
71}
72
73export type VerificationChannel = 'email' | 'discord' | 'telegram' | 'signal'
74
75export interface CreateAccountParams {
76 handle: string
77 email: string
78 password: string
79 inviteCode?: string
80 verificationChannel?: VerificationChannel
81 discordId?: string
82 telegramUsername?: string
83 signalNumber?: string
84}
85
86export interface CreateAccountResult {
87 handle: string
88 did: string
89 verificationRequired: boolean
90 verificationChannel: string
91}
92
93export interface ConfirmSignupResult {
94 accessJwt: string
95 refreshJwt: string
96 handle: string
97 did: string
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}