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