this repo has no description
1const API_BASE = '/xrpc' 2 3export class ApiError extends Error { 4 public did?: string 5 public reauthMethods?: string[] 6 constructor(public status: number, public error: string, message: string, did?: string, reauthMethods?: string[]) { 7 super(message) 8 this.name = 'ApiError' 9 this.did = did 10 this.reauthMethods = reauthMethods 11 } 12} 13 14async function xrpc<T>(method: string, options?: { 15 method?: 'GET' | 'POST' 16 params?: Record<string, string> 17 body?: unknown 18 token?: string 19}): Promise<T> { 20 const { method: httpMethod = 'GET', params, body, token } = options ?? {} 21 let url = `${API_BASE}/${method}` 22 if (params) { 23 const searchParams = new URLSearchParams(params) 24 url += `?${searchParams}` 25 } 26 const headers: Record<string, string> = {} 27 if (token) { 28 headers['Authorization'] = `Bearer ${token}` 29 } 30 if (body) { 31 headers['Content-Type'] = 'application/json' 32 } 33 const res = await fetch(url, { 34 method: httpMethod, 35 headers, 36 body: body ? JSON.stringify(body) : undefined, 37 }) 38 if (!res.ok) { 39 const err = await res.json().catch(() => ({ error: 'Unknown', message: res.statusText })) 40 throw new ApiError(res.status, err.error, err.message, err.did, err.reauthMethods) 41 } 42 return res.json() 43} 44 45export interface Session { 46 did: string 47 handle: string 48 email?: string 49 emailConfirmed?: boolean 50 preferredChannel?: string 51 preferredChannelVerified?: boolean 52 isAdmin?: boolean 53 active?: boolean 54 status?: 'active' | 'deactivated' 55 accessJwt: string 56 refreshJwt: string 57} 58 59export interface AppPassword { 60 name: string 61 createdAt: string 62} 63 64export interface InviteCode { 65 code: string 66 available: number 67 disabled: boolean 68 forAccount: string 69 createdBy: string 70 createdAt: string 71 uses: { usedBy: string; usedAt: string }[] 72} 73 74export type VerificationChannel = 'email' | 'discord' | 'telegram' | 'signal' 75 76export type DidType = 'plc' | 'web' | 'web-external' 77 78export interface CreateAccountParams { 79 handle: string 80 email: string 81 password: string 82 inviteCode?: string 83 didType?: DidType 84 did?: string 85 verificationChannel?: VerificationChannel 86 discordId?: string 87 telegramUsername?: string 88 signalNumber?: string 89} 90 91export interface CreateAccountResult { 92 handle: string 93 did: string 94 verificationRequired: boolean 95 verificationChannel: string 96} 97 98export interface ConfirmSignupResult { 99 accessJwt: string 100 refreshJwt: string 101 handle: string 102 did: string 103 email?: string 104 emailConfirmed?: boolean 105 preferredChannel?: string 106 preferredChannelVerified?: boolean 107} 108 109export const api = { 110 async createAccount(params: CreateAccountParams): Promise<CreateAccountResult> { 111 return xrpc('com.atproto.server.createAccount', { 112 method: 'POST', 113 body: { 114 handle: params.handle, 115 email: params.email, 116 password: params.password, 117 inviteCode: params.inviteCode, 118 didType: params.didType, 119 did: params.did, 120 verificationChannel: params.verificationChannel, 121 discordId: params.discordId, 122 telegramUsername: params.telegramUsername, 123 signalNumber: params.signalNumber, 124 }, 125 }) 126 }, 127 128 async confirmSignup(did: string, verificationCode: string): Promise<ConfirmSignupResult> { 129 return xrpc('com.atproto.server.confirmSignup', { 130 method: 'POST', 131 body: { did, verificationCode }, 132 }) 133 }, 134 135 async resendVerification(did: string): Promise<{ success: boolean }> { 136 return xrpc('com.atproto.server.resendVerification', { 137 method: 'POST', 138 body: { did }, 139 }) 140 }, 141 142 async createSession(identifier: string, password: string): Promise<Session> { 143 return xrpc('com.atproto.server.createSession', { 144 method: 'POST', 145 body: { identifier, password }, 146 }) 147 }, 148 149 async getSession(token: string): Promise<Session> { 150 return xrpc('com.atproto.server.getSession', { token }) 151 }, 152 153 async refreshSession(refreshJwt: string): Promise<Session> { 154 return xrpc('com.atproto.server.refreshSession', { 155 method: 'POST', 156 token: refreshJwt, 157 }) 158 }, 159 160 async deleteSession(token: string): Promise<void> { 161 await xrpc('com.atproto.server.deleteSession', { 162 method: 'POST', 163 token, 164 }) 165 }, 166 167 async listAppPasswords(token: string): Promise<{ passwords: AppPassword[] }> { 168 return xrpc('com.atproto.server.listAppPasswords', { token }) 169 }, 170 171 async createAppPassword(token: string, name: string): Promise<{ name: string; password: string; createdAt: string }> { 172 return xrpc('com.atproto.server.createAppPassword', { 173 method: 'POST', 174 token, 175 body: { name }, 176 }) 177 }, 178 179 async revokeAppPassword(token: string, name: string): Promise<void> { 180 await xrpc('com.atproto.server.revokeAppPassword', { 181 method: 'POST', 182 token, 183 body: { name }, 184 }) 185 }, 186 187 async getAccountInviteCodes(token: string): Promise<{ codes: InviteCode[] }> { 188 return xrpc('com.atproto.server.getAccountInviteCodes', { token }) 189 }, 190 191 async createInviteCode(token: string, useCount: number = 1): Promise<{ code: string }> { 192 return xrpc('com.atproto.server.createInviteCode', { 193 method: 'POST', 194 token, 195 body: { useCount }, 196 }) 197 }, 198 199 async requestPasswordReset(email: string): Promise<void> { 200 await xrpc('com.atproto.server.requestPasswordReset', { 201 method: 'POST', 202 body: { email }, 203 }) 204 }, 205 206 async resetPassword(token: string, password: string): Promise<void> { 207 await xrpc('com.atproto.server.resetPassword', { 208 method: 'POST', 209 body: { token, password }, 210 }) 211 }, 212 213 async requestEmailUpdate(token: string, email: string): Promise<{ tokenRequired: boolean }> { 214 return xrpc('com.atproto.server.requestEmailUpdate', { 215 method: 'POST', 216 token, 217 body: { email }, 218 }) 219 }, 220 221 async updateEmail(token: string, email: string, emailToken?: string): Promise<void> { 222 await xrpc('com.atproto.server.updateEmail', { 223 method: 'POST', 224 token, 225 body: { email, token: emailToken }, 226 }) 227 }, 228 229 async updateHandle(token: string, handle: string): Promise<void> { 230 await xrpc('com.atproto.identity.updateHandle', { 231 method: 'POST', 232 token, 233 body: { handle }, 234 }) 235 }, 236 237 async requestAccountDelete(token: string): Promise<void> { 238 await xrpc('com.atproto.server.requestAccountDelete', { 239 method: 'POST', 240 token, 241 }) 242 }, 243 244 async deleteAccount(did: string, password: string, deleteToken: string): Promise<void> { 245 await xrpc('com.atproto.server.deleteAccount', { 246 method: 'POST', 247 body: { did, password, token: deleteToken }, 248 }) 249 }, 250 251 async describeServer(): Promise<{ 252 availableUserDomains: string[] 253 inviteCodeRequired: boolean 254 links?: { privacyPolicy?: string; termsOfService?: string } 255 }> { 256 return xrpc('com.atproto.server.describeServer') 257 }, 258 259 async getNotificationPrefs(token: string): Promise<{ 260 preferredChannel: string 261 email: string 262 discordId: string | null 263 discordVerified: boolean 264 telegramUsername: string | null 265 telegramVerified: boolean 266 signalNumber: string | null 267 signalVerified: boolean 268 }> { 269 return xrpc('com.tranquil.account.getNotificationPrefs', { token }) 270 }, 271 272 async updateNotificationPrefs(token: string, prefs: { 273 preferredChannel?: string 274 discordId?: string 275 telegramUsername?: string 276 signalNumber?: string 277 }): Promise<{ success: boolean }> { 278 return xrpc('com.tranquil.account.updateNotificationPrefs', { 279 method: 'POST', 280 token, 281 body: prefs, 282 }) 283 }, 284 285 async confirmChannelVerification(token: string, channel: string, code: string): Promise<{ success: boolean }> { 286 return xrpc('com.tranquil.account.confirmChannelVerification', { 287 method: 'POST', 288 token, 289 body: { channel, code }, 290 }) 291 }, 292 293 async getNotificationHistory(token: string): Promise<{ 294 notifications: Array<{ 295 createdAt: string 296 channel: string 297 notificationType: string 298 status: string 299 subject: string | null 300 body: string 301 }> 302 }> { 303 return xrpc('com.tranquil.account.getNotificationHistory', { token }) 304 }, 305 306 async getServerStats(token: string): Promise<{ 307 userCount: number 308 repoCount: number 309 recordCount: number 310 blobStorageBytes: number 311 }> { 312 return xrpc('com.tranquil.admin.getServerStats', { token }) 313 }, 314 315 async changePassword(token: string, currentPassword: string, newPassword: string): Promise<void> { 316 await xrpc('com.tranquil.account.changePassword', { 317 method: 'POST', 318 token, 319 body: { currentPassword, newPassword }, 320 }) 321 }, 322 323 async removePassword(token: string): Promise<{ success: boolean }> { 324 return xrpc('com.tranquil.account.removePassword', { 325 method: 'POST', 326 token, 327 }) 328 }, 329 330 async getPasswordStatus(token: string): Promise<{ hasPassword: boolean }> { 331 return xrpc('com.tranquil.account.getPasswordStatus', { token }) 332 }, 333 334 async getLegacyLoginPreference(token: string): Promise<{ allowLegacyLogin: boolean; hasMfa: boolean }> { 335 return xrpc('com.tranquil.account.getLegacyLoginPreference', { token }) 336 }, 337 338 async updateLegacyLoginPreference(token: string, allowLegacyLogin: boolean): Promise<{ allowLegacyLogin: boolean }> { 339 return xrpc('com.tranquil.account.updateLegacyLoginPreference', { 340 method: 'POST', 341 token, 342 body: { allowLegacyLogin }, 343 }) 344 }, 345 346 async listSessions(token: string): Promise<{ 347 sessions: Array<{ 348 id: string 349 sessionType: string 350 clientName: string | null 351 createdAt: string 352 expiresAt: string 353 isCurrent: boolean 354 }> 355 }> { 356 return xrpc('com.tranquil.account.listSessions', { token }) 357 }, 358 359 async revokeSession(token: string, sessionId: string): Promise<void> { 360 await xrpc('com.tranquil.account.revokeSession', { 361 method: 'POST', 362 token, 363 body: { sessionId }, 364 }) 365 }, 366 367 async revokeAllSessions(token: string): Promise<{ revokedCount: number }> { 368 return xrpc('com.tranquil.account.revokeAllSessions', { 369 method: 'POST', 370 token, 371 }) 372 }, 373 374 async searchAccounts(token: string, options?: { 375 handle?: string 376 cursor?: string 377 limit?: number 378 }): Promise<{ 379 cursor?: string 380 accounts: Array<{ 381 did: string 382 handle: string 383 email?: string 384 indexedAt: string 385 emailConfirmedAt?: string 386 deactivatedAt?: string 387 }> 388 }> { 389 const params: Record<string, string> = {} 390 if (options?.handle) params.handle = options.handle 391 if (options?.cursor) params.cursor = options.cursor 392 if (options?.limit) params.limit = String(options.limit) 393 return xrpc('com.atproto.admin.searchAccounts', { token, params }) 394 }, 395 396 async getInviteCodes(token: string, options?: { 397 sort?: 'recent' | 'usage' 398 cursor?: string 399 limit?: number 400 }): Promise<{ 401 cursor?: string 402 codes: Array<{ 403 code: string 404 available: number 405 disabled: boolean 406 forAccount: string 407 createdBy: string 408 createdAt: string 409 uses: Array<{ usedBy: string; usedAt: string }> 410 }> 411 }> { 412 const params: Record<string, string> = {} 413 if (options?.sort) params.sort = options.sort 414 if (options?.cursor) params.cursor = options.cursor 415 if (options?.limit) params.limit = String(options.limit) 416 return xrpc('com.atproto.admin.getInviteCodes', { token, params }) 417 }, 418 419 async disableInviteCodes(token: string, codes?: string[], accounts?: string[]): Promise<void> { 420 await xrpc('com.atproto.admin.disableInviteCodes', { 421 method: 'POST', 422 token, 423 body: { codes, accounts }, 424 }) 425 }, 426 427 async getAccountInfo(token: string, did: string): Promise<{ 428 did: string 429 handle: string 430 email?: string 431 indexedAt: string 432 emailConfirmedAt?: string 433 invitesDisabled?: boolean 434 deactivatedAt?: string 435 }> { 436 return xrpc('com.atproto.admin.getAccountInfo', { token, params: { did } }) 437 }, 438 439 async disableAccountInvites(token: string, account: string): Promise<void> { 440 await xrpc('com.atproto.admin.disableAccountInvites', { 441 method: 'POST', 442 token, 443 body: { account }, 444 }) 445 }, 446 447 async enableAccountInvites(token: string, account: string): Promise<void> { 448 await xrpc('com.atproto.admin.enableAccountInvites', { 449 method: 'POST', 450 token, 451 body: { account }, 452 }) 453 }, 454 455 async adminDeleteAccount(token: string, did: string): Promise<void> { 456 await xrpc('com.atproto.admin.deleteAccount', { 457 method: 'POST', 458 token, 459 body: { did }, 460 }) 461 }, 462 463 async describeRepo(token: string, repo: string): Promise<{ 464 handle: string 465 did: string 466 didDoc: unknown 467 collections: string[] 468 handleIsCorrect: boolean 469 }> { 470 return xrpc('com.atproto.repo.describeRepo', { 471 token, 472 params: { repo }, 473 }) 474 }, 475 476 async listRecords(token: string, repo: string, collection: string, options?: { 477 limit?: number 478 cursor?: string 479 reverse?: boolean 480 }): Promise<{ 481 records: Array<{ uri: string; cid: string; value: unknown }> 482 cursor?: string 483 }> { 484 const params: Record<string, string> = { repo, collection } 485 if (options?.limit) params.limit = String(options.limit) 486 if (options?.cursor) params.cursor = options.cursor 487 if (options?.reverse) params.reverse = 'true' 488 return xrpc('com.atproto.repo.listRecords', { token, params }) 489 }, 490 491 async getRecord(token: string, repo: string, collection: string, rkey: string): Promise<{ 492 uri: string 493 cid: string 494 value: unknown 495 }> { 496 return xrpc('com.atproto.repo.getRecord', { 497 token, 498 params: { repo, collection, rkey }, 499 }) 500 }, 501 502 async createRecord(token: string, repo: string, collection: string, record: unknown, rkey?: string): Promise<{ 503 uri: string 504 cid: string 505 }> { 506 return xrpc('com.atproto.repo.createRecord', { 507 method: 'POST', 508 token, 509 body: { repo, collection, record, rkey }, 510 }) 511 }, 512 513 async putRecord(token: string, repo: string, collection: string, rkey: string, record: unknown): Promise<{ 514 uri: string 515 cid: string 516 }> { 517 return xrpc('com.atproto.repo.putRecord', { 518 method: 'POST', 519 token, 520 body: { repo, collection, rkey, record }, 521 }) 522 }, 523 524 async deleteRecord(token: string, repo: string, collection: string, rkey: string): Promise<void> { 525 await xrpc('com.atproto.repo.deleteRecord', { 526 method: 'POST', 527 token, 528 body: { repo, collection, rkey }, 529 }) 530 }, 531 532 async getTotpStatus(token: string): Promise<{ enabled: boolean; hasBackupCodes: boolean }> { 533 return xrpc('com.atproto.server.getTotpStatus', { token }) 534 }, 535 536 async createTotpSecret(token: string): Promise<{ uri: string; qrBase64: string }> { 537 return xrpc('com.atproto.server.createTotpSecret', { method: 'POST', token }) 538 }, 539 540 async enableTotp(token: string, code: string): Promise<{ success: boolean; backupCodes: string[] }> { 541 return xrpc('com.atproto.server.enableTotp', { 542 method: 'POST', 543 token, 544 body: { code }, 545 }) 546 }, 547 548 async disableTotp(token: string, password: string, code: string): Promise<{ success: boolean }> { 549 return xrpc('com.atproto.server.disableTotp', { 550 method: 'POST', 551 token, 552 body: { password, code }, 553 }) 554 }, 555 556 async regenerateBackupCodes(token: string, password: string, code: string): Promise<{ backupCodes: string[] }> { 557 return xrpc('com.atproto.server.regenerateBackupCodes', { 558 method: 'POST', 559 token, 560 body: { password, code }, 561 }) 562 }, 563 564 async startPasskeyRegistration(token: string, friendlyName?: string): Promise<{ options: unknown }> { 565 return xrpc('com.atproto.server.startPasskeyRegistration', { 566 method: 'POST', 567 token, 568 body: { friendlyName }, 569 }) 570 }, 571 572 async finishPasskeyRegistration(token: string, credential: unknown, friendlyName?: string): Promise<{ id: string; credentialId: string }> { 573 return xrpc('com.atproto.server.finishPasskeyRegistration', { 574 method: 'POST', 575 token, 576 body: { credential, friendlyName }, 577 }) 578 }, 579 580 async listPasskeys(token: string): Promise<{ 581 passkeys: Array<{ 582 id: string 583 credentialId: string 584 friendlyName: string | null 585 createdAt: string 586 lastUsed: string | null 587 }> 588 }> { 589 return xrpc('com.atproto.server.listPasskeys', { token }) 590 }, 591 592 async deletePasskey(token: string, id: string): Promise<void> { 593 await xrpc('com.atproto.server.deletePasskey', { 594 method: 'POST', 595 token, 596 body: { id }, 597 }) 598 }, 599 600 async updatePasskey(token: string, id: string, friendlyName: string): Promise<void> { 601 await xrpc('com.atproto.server.updatePasskey', { 602 method: 'POST', 603 token, 604 body: { id, friendlyName }, 605 }) 606 }, 607 608 async listTrustedDevices(token: string): Promise<{ 609 devices: Array<{ 610 id: string 611 userAgent: string | null 612 friendlyName: string | null 613 trustedAt: string | null 614 trustedUntil: string | null 615 lastSeenAt: string 616 }> 617 }> { 618 return xrpc('com.tranquil.account.listTrustedDevices', { token }) 619 }, 620 621 async revokeTrustedDevice(token: string, deviceId: string): Promise<{ success: boolean }> { 622 return xrpc('com.tranquil.account.revokeTrustedDevice', { 623 method: 'POST', 624 token, 625 body: { deviceId }, 626 }) 627 }, 628 629 async updateTrustedDevice(token: string, deviceId: string, friendlyName: string): Promise<{ success: boolean }> { 630 return xrpc('com.tranquil.account.updateTrustedDevice', { 631 method: 'POST', 632 token, 633 body: { deviceId, friendlyName }, 634 }) 635 }, 636 637 async getReauthStatus(token: string): Promise<{ 638 requiresReauth: boolean 639 lastReauthAt: string | null 640 availableMethods: string[] 641 }> { 642 return xrpc('com.tranquil.account.getReauthStatus', { token }) 643 }, 644 645 async reauthPassword(token: string, password: string): Promise<{ success: boolean; reauthAt: string }> { 646 return xrpc('com.tranquil.account.reauthPassword', { 647 method: 'POST', 648 token, 649 body: { password }, 650 }) 651 }, 652 653 async reauthTotp(token: string, code: string): Promise<{ success: boolean; reauthAt: string }> { 654 return xrpc('com.tranquil.account.reauthTotp', { 655 method: 'POST', 656 token, 657 body: { code }, 658 }) 659 }, 660 661 async reauthPasskeyStart(token: string): Promise<{ options: unknown }> { 662 return xrpc('com.tranquil.account.reauthPasskeyStart', { 663 method: 'POST', 664 token, 665 }) 666 }, 667 668 async reauthPasskeyFinish(token: string, credential: unknown): Promise<{ success: boolean; reauthAt: string }> { 669 return xrpc('com.tranquil.account.reauthPasskeyFinish', { 670 method: 'POST', 671 token, 672 body: { credential }, 673 }) 674 }, 675 676 async createPasskeyAccount(params: { 677 handle: string 678 email?: string 679 inviteCode?: string 680 didType?: DidType 681 did?: string 682 signingKey?: string 683 verificationChannel?: VerificationChannel 684 discordId?: string 685 telegramUsername?: string 686 signalNumber?: string 687 }): Promise<{ 688 did: string 689 handle: string 690 setupToken: string 691 setupExpiresAt: string 692 }> { 693 return xrpc('com.tranquil.account.createPasskeyAccount', { 694 method: 'POST', 695 body: params, 696 }) 697 }, 698 699 async startPasskeyRegistrationForSetup(did: string, setupToken: string, friendlyName?: string): Promise<{ options: unknown }> { 700 return xrpc('com.tranquil.account.startPasskeyRegistrationForSetup', { 701 method: 'POST', 702 body: { did, setupToken, friendlyName }, 703 }) 704 }, 705 706 async completePasskeySetup(did: string, setupToken: string, passkeyCredential: unknown, passkeyFriendlyName?: string): Promise<{ 707 did: string 708 handle: string 709 appPassword: string 710 appPasswordName: string 711 }> { 712 return xrpc('com.tranquil.account.completePasskeySetup', { 713 method: 'POST', 714 body: { did, setupToken, passkeyCredential, passkeyFriendlyName }, 715 }) 716 }, 717 718 async requestPasskeyRecovery(email: string): Promise<{ success: boolean }> { 719 return xrpc('com.tranquil.account.requestPasskeyRecovery', { 720 method: 'POST', 721 body: { email }, 722 }) 723 }, 724 725 async recoverPasskeyAccount(did: string, recoveryToken: string, newPassword: string): Promise<{ success: boolean }> { 726 return xrpc('com.tranquil.account.recoverPasskeyAccount', { 727 method: 'POST', 728 body: { did, recoveryToken, newPassword }, 729 }) 730 }, 731}