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( 7 public status: number, 8 public error: string, 9 message: string, 10 did?: string, 11 reauthMethods?: string[], 12 ) { 13 super(message); 14 this.name = "ApiError"; 15 this.did = did; 16 this.reauthMethods = reauthMethods; 17 } 18} 19 20let tokenRefreshCallback: (() => Promise<string | null>) | null = null; 21 22export function setTokenRefreshCallback( 23 callback: () => Promise<string | null>, 24) { 25 tokenRefreshCallback = callback; 26} 27 28async function xrpc<T>(method: string, options?: { 29 method?: "GET" | "POST"; 30 params?: Record<string, string>; 31 body?: unknown; 32 token?: string; 33 skipRetry?: boolean; 34}): Promise<T> { 35 const { method: httpMethod = "GET", params, body, token, skipRetry } = 36 options ?? {}; 37 let url = `${API_BASE}/${method}`; 38 if (params) { 39 const searchParams = new URLSearchParams(params); 40 url += `?${searchParams}`; 41 } 42 const headers: Record<string, string> = {}; 43 if (token) { 44 headers["Authorization"] = `Bearer ${token}`; 45 } 46 if (body) { 47 headers["Content-Type"] = "application/json"; 48 } 49 const res = await fetch(url, { 50 method: httpMethod, 51 headers, 52 body: body ? JSON.stringify(body) : undefined, 53 }); 54 if (!res.ok) { 55 const err = await res.json().catch(() => ({ 56 error: "Unknown", 57 message: res.statusText, 58 })); 59 if ( 60 res.status === 401 && 61 (err.error === "AuthenticationFailed" || err.error === "ExpiredToken") && 62 token && tokenRefreshCallback && !skipRetry 63 ) { 64 const newToken = await tokenRefreshCallback(); 65 if (newToken && newToken !== token) { 66 return xrpc(method, { ...options, token: newToken, skipRetry: true }); 67 } 68 } 69 throw new ApiError( 70 res.status, 71 err.error, 72 err.message, 73 err.did, 74 err.reauthMethods, 75 ); 76 } 77 return res.json(); 78} 79 80export interface Session { 81 did: string; 82 handle: string; 83 email?: string; 84 emailConfirmed?: boolean; 85 preferredChannel?: string; 86 preferredChannelVerified?: boolean; 87 isAdmin?: boolean; 88 active?: boolean; 89 status?: "active" | "deactivated" | "migrated"; 90 migratedToPds?: string; 91 migratedAt?: string; 92 accessJwt: string; 93 refreshJwt: string; 94} 95 96export interface VerificationMethod { 97 id: string; 98 type: string; 99 publicKeyMultibase: string; 100} 101 102export interface DidDocument { 103 "@context": string[]; 104 id: string; 105 alsoKnownAs: string[]; 106 verificationMethod: Array<{ 107 id: string; 108 type: string; 109 controller: string; 110 publicKeyMultibase: string; 111 }>; 112 service: Array<{ 113 id: string; 114 type: string; 115 serviceEndpoint: string; 116 }>; 117} 118 119export interface AppPassword { 120 name: string; 121 createdAt: string; 122 scopes?: string; 123 createdByController?: string; 124} 125 126export interface InviteCode { 127 code: string; 128 available: number; 129 disabled: boolean; 130 forAccount: string; 131 createdBy: string; 132 createdAt: string; 133 uses: { usedBy: string; usedByHandle?: string; usedAt: string }[]; 134} 135 136export type VerificationChannel = "email" | "discord" | "telegram" | "signal"; 137 138export type DidType = "plc" | "web" | "web-external"; 139 140export interface CreateAccountParams { 141 handle: string; 142 email: string; 143 password: string; 144 inviteCode?: string; 145 didType?: DidType; 146 did?: string; 147 signingKey?: string; 148 verificationChannel?: VerificationChannel; 149 discordId?: string; 150 telegramUsername?: string; 151 signalNumber?: string; 152} 153 154export interface CreateAccountResult { 155 handle: string; 156 did: string; 157 verificationRequired: boolean; 158 verificationChannel: string; 159} 160 161export interface ConfirmSignupResult { 162 accessJwt: string; 163 refreshJwt: string; 164 handle: string; 165 did: string; 166 email?: string; 167 emailConfirmed?: boolean; 168 preferredChannel?: string; 169 preferredChannelVerified?: boolean; 170} 171 172export const api = { 173 async createAccount( 174 params: CreateAccountParams, 175 byodToken?: string, 176 ): Promise<CreateAccountResult> { 177 const url = `${API_BASE}/com.atproto.server.createAccount`; 178 const headers: Record<string, string> = { 179 "Content-Type": "application/json", 180 }; 181 if (byodToken) { 182 headers["Authorization"] = `Bearer ${byodToken}`; 183 } 184 const response = await fetch(url, { 185 method: "POST", 186 headers, 187 body: JSON.stringify({ 188 handle: params.handle, 189 email: params.email, 190 password: params.password, 191 inviteCode: params.inviteCode, 192 didType: params.didType, 193 did: params.did, 194 signingKey: params.signingKey, 195 verificationChannel: params.verificationChannel, 196 discordId: params.discordId, 197 telegramUsername: params.telegramUsername, 198 signalNumber: params.signalNumber, 199 }), 200 }); 201 const data = await response.json(); 202 if (!response.ok) { 203 throw new ApiError(response.status, data.error, data.message); 204 } 205 return data; 206 }, 207 208 async confirmSignup( 209 did: string, 210 verificationCode: string, 211 ): Promise<ConfirmSignupResult> { 212 return xrpc("com.atproto.server.confirmSignup", { 213 method: "POST", 214 body: { did, verificationCode }, 215 }); 216 }, 217 218 async resendVerification(did: string): Promise<{ success: boolean }> { 219 return xrpc("com.atproto.server.resendVerification", { 220 method: "POST", 221 body: { did }, 222 }); 223 }, 224 225 async createSession(identifier: string, password: string): Promise<Session> { 226 return xrpc("com.atproto.server.createSession", { 227 method: "POST", 228 body: { identifier, password }, 229 }); 230 }, 231 232 async getSession(token: string): Promise<Session> { 233 return xrpc("com.atproto.server.getSession", { token }); 234 }, 235 236 async refreshSession(refreshJwt: string): Promise<Session> { 237 return xrpc("com.atproto.server.refreshSession", { 238 method: "POST", 239 token: refreshJwt, 240 }); 241 }, 242 243 async deleteSession(token: string): Promise<void> { 244 await xrpc("com.atproto.server.deleteSession", { 245 method: "POST", 246 token, 247 }); 248 }, 249 250 async listAppPasswords(token: string): Promise<{ passwords: AppPassword[] }> { 251 return xrpc("com.atproto.server.listAppPasswords", { token }); 252 }, 253 254 async createAppPassword( 255 token: string, 256 name: string, 257 scopes?: string, 258 ): Promise< 259 { name: string; password: string; createdAt: string; scopes?: string } 260 > { 261 return xrpc("com.atproto.server.createAppPassword", { 262 method: "POST", 263 token, 264 body: { name, scopes }, 265 }); 266 }, 267 268 async revokeAppPassword(token: string, name: string): Promise<void> { 269 await xrpc("com.atproto.server.revokeAppPassword", { 270 method: "POST", 271 token, 272 body: { name }, 273 }); 274 }, 275 276 async getAccountInviteCodes(token: string): Promise<{ codes: InviteCode[] }> { 277 return xrpc("com.atproto.server.getAccountInviteCodes", { token }); 278 }, 279 280 async createInviteCode( 281 token: string, 282 useCount: number = 1, 283 ): Promise<{ code: string }> { 284 return xrpc("com.atproto.server.createInviteCode", { 285 method: "POST", 286 token, 287 body: { useCount }, 288 }); 289 }, 290 291 async requestPasswordReset(email: string): Promise<void> { 292 await xrpc("com.atproto.server.requestPasswordReset", { 293 method: "POST", 294 body: { email }, 295 }); 296 }, 297 298 async resetPassword(token: string, password: string): Promise<void> { 299 await xrpc("com.atproto.server.resetPassword", { 300 method: "POST", 301 body: { token, password }, 302 }); 303 }, 304 305 async requestEmailUpdate( 306 token: string, 307 ): Promise<{ tokenRequired: boolean }> { 308 return xrpc("com.atproto.server.requestEmailUpdate", { 309 method: "POST", 310 token, 311 }); 312 }, 313 314 async updateEmail( 315 token: string, 316 email: string, 317 emailToken?: string, 318 ): Promise<void> { 319 await xrpc("com.atproto.server.updateEmail", { 320 method: "POST", 321 token, 322 body: { email, token: emailToken }, 323 }); 324 }, 325 326 async updateHandle(token: string, handle: string): Promise<void> { 327 await xrpc("com.atproto.identity.updateHandle", { 328 method: "POST", 329 token, 330 body: { handle }, 331 }); 332 }, 333 334 async requestAccountDelete(token: string): Promise<void> { 335 await xrpc("com.atproto.server.requestAccountDelete", { 336 method: "POST", 337 token, 338 }); 339 }, 340 341 async deleteAccount( 342 did: string, 343 password: string, 344 deleteToken: string, 345 ): Promise<void> { 346 await xrpc("com.atproto.server.deleteAccount", { 347 method: "POST", 348 body: { did, password, token: deleteToken }, 349 }); 350 }, 351 352 async describeServer(): Promise<{ 353 availableUserDomains: string[]; 354 inviteCodeRequired: boolean; 355 links?: { privacyPolicy?: string; termsOfService?: string }; 356 version?: string; 357 availableCommsChannels?: string[]; 358 selfHostedDidWebEnabled?: boolean; 359 }> { 360 return xrpc("com.atproto.server.describeServer"); 361 }, 362 363 async listRepos(limit?: number): Promise<{ 364 repos: Array<{ did: string; head: string; rev: string }>; 365 cursor?: string; 366 }> { 367 const params: Record<string, string> = {}; 368 if (limit) params.limit = String(limit); 369 return xrpc("com.atproto.sync.listRepos", { params }); 370 }, 371 372 async getNotificationPrefs(token: string): Promise<{ 373 preferredChannel: string; 374 email: string; 375 discordId: string | null; 376 discordVerified: boolean; 377 telegramUsername: string | null; 378 telegramVerified: boolean; 379 signalNumber: string | null; 380 signalVerified: boolean; 381 }> { 382 return xrpc("com.tranquil.account.getNotificationPrefs", { token }); 383 }, 384 385 async updateNotificationPrefs(token: string, prefs: { 386 preferredChannel?: string; 387 discordId?: string; 388 telegramUsername?: string; 389 signalNumber?: string; 390 }): Promise<{ success: boolean }> { 391 return xrpc("com.tranquil.account.updateNotificationPrefs", { 392 method: "POST", 393 token, 394 body: prefs, 395 }); 396 }, 397 398 async confirmChannelVerification( 399 token: string, 400 channel: string, 401 identifier: string, 402 code: string, 403 ): Promise<{ success: boolean }> { 404 return xrpc("com.tranquil.account.confirmChannelVerification", { 405 method: "POST", 406 token, 407 body: { channel, identifier, code }, 408 }); 409 }, 410 411 async getNotificationHistory(token: string): Promise<{ 412 notifications: Array<{ 413 createdAt: string; 414 channel: string; 415 notificationType: string; 416 status: string; 417 subject: string | null; 418 body: string; 419 }>; 420 }> { 421 return xrpc("com.tranquil.account.getNotificationHistory", { token }); 422 }, 423 424 async getServerStats(token: string): Promise<{ 425 userCount: number; 426 repoCount: number; 427 recordCount: number; 428 blobStorageBytes: number; 429 }> { 430 return xrpc("com.tranquil.admin.getServerStats", { token }); 431 }, 432 433 async getServerConfig(): Promise<{ 434 serverName: string; 435 primaryColor: string | null; 436 primaryColorDark: string | null; 437 secondaryColor: string | null; 438 secondaryColorDark: string | null; 439 logoCid: string | null; 440 }> { 441 return xrpc("com.tranquil.server.getConfig"); 442 }, 443 444 async updateServerConfig( 445 token: string, 446 config: { 447 serverName?: string; 448 primaryColor?: string; 449 primaryColorDark?: string; 450 secondaryColor?: string; 451 secondaryColorDark?: string; 452 logoCid?: string; 453 }, 454 ): Promise<{ success: boolean }> { 455 return xrpc("com.tranquil.admin.updateServerConfig", { 456 method: "POST", 457 token, 458 body: config, 459 }); 460 }, 461 462 async uploadBlob( 463 token: string, 464 file: File, 465 ): Promise< 466 { 467 blob: { 468 $type: string; 469 ref: { $link: string }; 470 mimeType: string; 471 size: number; 472 }; 473 } 474 > { 475 const res = await fetch("/xrpc/com.atproto.repo.uploadBlob", { 476 method: "POST", 477 headers: { 478 "Authorization": `Bearer ${token}`, 479 "Content-Type": file.type, 480 }, 481 body: file, 482 }); 483 if (!res.ok) { 484 const err = await res.json().catch(() => ({ 485 error: "Unknown", 486 message: res.statusText, 487 })); 488 throw new ApiError(res.status, err.error, err.message); 489 } 490 return res.json(); 491 }, 492 493 async changePassword( 494 token: string, 495 currentPassword: string, 496 newPassword: string, 497 ): Promise<void> { 498 await xrpc("com.tranquil.account.changePassword", { 499 method: "POST", 500 token, 501 body: { currentPassword, newPassword }, 502 }); 503 }, 504 505 async removePassword(token: string): Promise<{ success: boolean }> { 506 return xrpc("com.tranquil.account.removePassword", { 507 method: "POST", 508 token, 509 }); 510 }, 511 512 async getPasswordStatus(token: string): Promise<{ hasPassword: boolean }> { 513 return xrpc("com.tranquil.account.getPasswordStatus", { token }); 514 }, 515 516 async getLegacyLoginPreference( 517 token: string, 518 ): Promise<{ allowLegacyLogin: boolean; hasMfa: boolean }> { 519 return xrpc("com.tranquil.account.getLegacyLoginPreference", { token }); 520 }, 521 522 async updateLegacyLoginPreference( 523 token: string, 524 allowLegacyLogin: boolean, 525 ): Promise<{ allowLegacyLogin: boolean }> { 526 return xrpc("com.tranquil.account.updateLegacyLoginPreference", { 527 method: "POST", 528 token, 529 body: { allowLegacyLogin }, 530 }); 531 }, 532 533 async updateLocale( 534 token: string, 535 preferredLocale: string, 536 ): Promise<{ preferredLocale: string }> { 537 return xrpc("com.tranquil.account.updateLocale", { 538 method: "POST", 539 token, 540 body: { preferredLocale }, 541 }); 542 }, 543 544 async listSessions(token: string): Promise<{ 545 sessions: Array<{ 546 id: string; 547 sessionType: string; 548 clientName: string | null; 549 createdAt: string; 550 expiresAt: string; 551 isCurrent: boolean; 552 }>; 553 }> { 554 return xrpc("com.tranquil.account.listSessions", { token }); 555 }, 556 557 async revokeSession(token: string, sessionId: string): Promise<void> { 558 await xrpc("com.tranquil.account.revokeSession", { 559 method: "POST", 560 token, 561 body: { sessionId }, 562 }); 563 }, 564 565 async revokeAllSessions(token: string): Promise<{ revokedCount: number }> { 566 return xrpc("com.tranquil.account.revokeAllSessions", { 567 method: "POST", 568 token, 569 }); 570 }, 571 572 async searchAccounts(token: string, options?: { 573 handle?: string; 574 cursor?: string; 575 limit?: number; 576 }): Promise<{ 577 cursor?: string; 578 accounts: Array<{ 579 did: string; 580 handle: string; 581 email?: string; 582 indexedAt: string; 583 emailConfirmedAt?: string; 584 deactivatedAt?: string; 585 }>; 586 }> { 587 const params: Record<string, string> = {}; 588 if (options?.handle) params.handle = options.handle; 589 if (options?.cursor) params.cursor = options.cursor; 590 if (options?.limit) params.limit = String(options.limit); 591 return xrpc("com.atproto.admin.searchAccounts", { token, params }); 592 }, 593 594 async getInviteCodes(token: string, options?: { 595 sort?: "recent" | "usage"; 596 cursor?: string; 597 limit?: number; 598 }): Promise<{ 599 cursor?: string; 600 codes: Array<{ 601 code: string; 602 available: number; 603 disabled: boolean; 604 forAccount: string; 605 createdBy: string; 606 createdAt: string; 607 uses: Array<{ usedBy: string; usedAt: string }>; 608 }>; 609 }> { 610 const params: Record<string, string> = {}; 611 if (options?.sort) params.sort = options.sort; 612 if (options?.cursor) params.cursor = options.cursor; 613 if (options?.limit) params.limit = String(options.limit); 614 return xrpc("com.atproto.admin.getInviteCodes", { token, params }); 615 }, 616 617 async disableInviteCodes( 618 token: string, 619 codes?: string[], 620 accounts?: string[], 621 ): Promise<void> { 622 await xrpc("com.atproto.admin.disableInviteCodes", { 623 method: "POST", 624 token, 625 body: { codes, accounts }, 626 }); 627 }, 628 629 async getAccountInfo(token: string, did: string): Promise<{ 630 did: string; 631 handle: string; 632 email?: string; 633 indexedAt: string; 634 emailConfirmedAt?: string; 635 invitesDisabled?: boolean; 636 deactivatedAt?: string; 637 }> { 638 return xrpc("com.atproto.admin.getAccountInfo", { token, params: { did } }); 639 }, 640 641 async disableAccountInvites(token: string, account: string): Promise<void> { 642 await xrpc("com.atproto.admin.disableAccountInvites", { 643 method: "POST", 644 token, 645 body: { account }, 646 }); 647 }, 648 649 async enableAccountInvites(token: string, account: string): Promise<void> { 650 await xrpc("com.atproto.admin.enableAccountInvites", { 651 method: "POST", 652 token, 653 body: { account }, 654 }); 655 }, 656 657 async adminDeleteAccount(token: string, did: string): Promise<void> { 658 await xrpc("com.atproto.admin.deleteAccount", { 659 method: "POST", 660 token, 661 body: { did }, 662 }); 663 }, 664 665 async describeRepo(token: string, repo: string): Promise<{ 666 handle: string; 667 did: string; 668 didDoc: unknown; 669 collections: string[]; 670 handleIsCorrect: boolean; 671 }> { 672 return xrpc("com.atproto.repo.describeRepo", { 673 token, 674 params: { repo }, 675 }); 676 }, 677 678 async listRecords(token: string, repo: string, collection: string, options?: { 679 limit?: number; 680 cursor?: string; 681 reverse?: boolean; 682 }): Promise<{ 683 records: Array<{ uri: string; cid: string; value: unknown }>; 684 cursor?: string; 685 }> { 686 const params: Record<string, string> = { repo, collection }; 687 if (options?.limit) params.limit = String(options.limit); 688 if (options?.cursor) params.cursor = options.cursor; 689 if (options?.reverse) params.reverse = "true"; 690 return xrpc("com.atproto.repo.listRecords", { token, params }); 691 }, 692 693 async getRecord( 694 token: string, 695 repo: string, 696 collection: string, 697 rkey: string, 698 ): Promise<{ 699 uri: string; 700 cid: string; 701 value: unknown; 702 }> { 703 return xrpc("com.atproto.repo.getRecord", { 704 token, 705 params: { repo, collection, rkey }, 706 }); 707 }, 708 709 async createRecord( 710 token: string, 711 repo: string, 712 collection: string, 713 record: unknown, 714 rkey?: string, 715 ): Promise<{ 716 uri: string; 717 cid: string; 718 }> { 719 return xrpc("com.atproto.repo.createRecord", { 720 method: "POST", 721 token, 722 body: { repo, collection, record, rkey }, 723 }); 724 }, 725 726 async putRecord( 727 token: string, 728 repo: string, 729 collection: string, 730 rkey: string, 731 record: unknown, 732 ): Promise<{ 733 uri: string; 734 cid: string; 735 }> { 736 return xrpc("com.atproto.repo.putRecord", { 737 method: "POST", 738 token, 739 body: { repo, collection, rkey, record }, 740 }); 741 }, 742 743 async deleteRecord( 744 token: string, 745 repo: string, 746 collection: string, 747 rkey: string, 748 ): Promise<void> { 749 await xrpc("com.atproto.repo.deleteRecord", { 750 method: "POST", 751 token, 752 body: { repo, collection, rkey }, 753 }); 754 }, 755 756 async getTotpStatus( 757 token: string, 758 ): Promise<{ enabled: boolean; hasBackupCodes: boolean }> { 759 return xrpc("com.atproto.server.getTotpStatus", { token }); 760 }, 761 762 async createTotpSecret( 763 token: string, 764 ): Promise<{ uri: string; qrBase64: string }> { 765 return xrpc("com.atproto.server.createTotpSecret", { 766 method: "POST", 767 token, 768 }); 769 }, 770 771 async enableTotp( 772 token: string, 773 code: string, 774 ): Promise<{ success: boolean; backupCodes: string[] }> { 775 return xrpc("com.atproto.server.enableTotp", { 776 method: "POST", 777 token, 778 body: { code }, 779 }); 780 }, 781 782 async disableTotp( 783 token: string, 784 password: string, 785 code: string, 786 ): Promise<{ success: boolean }> { 787 return xrpc("com.atproto.server.disableTotp", { 788 method: "POST", 789 token, 790 body: { password, code }, 791 }); 792 }, 793 794 async regenerateBackupCodes( 795 token: string, 796 password: string, 797 code: string, 798 ): Promise<{ backupCodes: string[] }> { 799 return xrpc("com.atproto.server.regenerateBackupCodes", { 800 method: "POST", 801 token, 802 body: { password, code }, 803 }); 804 }, 805 806 async startPasskeyRegistration( 807 token: string, 808 friendlyName?: string, 809 ): Promise<{ options: unknown }> { 810 return xrpc("com.atproto.server.startPasskeyRegistration", { 811 method: "POST", 812 token, 813 body: { friendlyName }, 814 }); 815 }, 816 817 async finishPasskeyRegistration( 818 token: string, 819 credential: unknown, 820 friendlyName?: string, 821 ): Promise<{ id: string; credentialId: string }> { 822 return xrpc("com.atproto.server.finishPasskeyRegistration", { 823 method: "POST", 824 token, 825 body: { credential, friendlyName }, 826 }); 827 }, 828 829 async listPasskeys(token: string): Promise<{ 830 passkeys: Array<{ 831 id: string; 832 credentialId: string; 833 friendlyName: string | null; 834 createdAt: string; 835 lastUsed: string | null; 836 }>; 837 }> { 838 return xrpc("com.atproto.server.listPasskeys", { token }); 839 }, 840 841 async deletePasskey(token: string, id: string): Promise<void> { 842 await xrpc("com.atproto.server.deletePasskey", { 843 method: "POST", 844 token, 845 body: { id }, 846 }); 847 }, 848 849 async updatePasskey( 850 token: string, 851 id: string, 852 friendlyName: string, 853 ): Promise<void> { 854 await xrpc("com.atproto.server.updatePasskey", { 855 method: "POST", 856 token, 857 body: { id, friendlyName }, 858 }); 859 }, 860 861 async listTrustedDevices(token: string): Promise<{ 862 devices: Array<{ 863 id: string; 864 userAgent: string | null; 865 friendlyName: string | null; 866 trustedAt: string | null; 867 trustedUntil: string | null; 868 lastSeenAt: string; 869 }>; 870 }> { 871 return xrpc("com.tranquil.account.listTrustedDevices", { token }); 872 }, 873 874 async revokeTrustedDevice( 875 token: string, 876 deviceId: string, 877 ): Promise<{ success: boolean }> { 878 return xrpc("com.tranquil.account.revokeTrustedDevice", { 879 method: "POST", 880 token, 881 body: { deviceId }, 882 }); 883 }, 884 885 async updateTrustedDevice( 886 token: string, 887 deviceId: string, 888 friendlyName: string, 889 ): Promise<{ success: boolean }> { 890 return xrpc("com.tranquil.account.updateTrustedDevice", { 891 method: "POST", 892 token, 893 body: { deviceId, friendlyName }, 894 }); 895 }, 896 897 async getReauthStatus(token: string): Promise<{ 898 requiresReauth: boolean; 899 lastReauthAt: string | null; 900 availableMethods: string[]; 901 }> { 902 return xrpc("com.tranquil.account.getReauthStatus", { token }); 903 }, 904 905 async reauthPassword( 906 token: string, 907 password: string, 908 ): Promise<{ success: boolean; reauthAt: string }> { 909 return xrpc("com.tranquil.account.reauthPassword", { 910 method: "POST", 911 token, 912 body: { password }, 913 }); 914 }, 915 916 async reauthTotp( 917 token: string, 918 code: string, 919 ): Promise<{ success: boolean; reauthAt: string }> { 920 return xrpc("com.tranquil.account.reauthTotp", { 921 method: "POST", 922 token, 923 body: { code }, 924 }); 925 }, 926 927 async reauthPasskeyStart(token: string): Promise<{ options: unknown }> { 928 return xrpc("com.tranquil.account.reauthPasskeyStart", { 929 method: "POST", 930 token, 931 }); 932 }, 933 934 async reauthPasskeyFinish( 935 token: string, 936 credential: unknown, 937 ): Promise<{ success: boolean; reauthAt: string }> { 938 return xrpc("com.tranquil.account.reauthPasskeyFinish", { 939 method: "POST", 940 token, 941 body: { credential }, 942 }); 943 }, 944 945 async reserveSigningKey(did?: string): Promise<{ signingKey: string }> { 946 return xrpc("com.atproto.server.reserveSigningKey", { 947 method: "POST", 948 body: { did }, 949 }); 950 }, 951 952 async getRecommendedDidCredentials(token: string): Promise<{ 953 rotationKeys?: string[]; 954 alsoKnownAs?: string[]; 955 verificationMethods?: { atproto?: string }; 956 services?: { atproto_pds?: { type: string; endpoint: string } }; 957 }> { 958 return xrpc("com.atproto.identity.getRecommendedDidCredentials", { token }); 959 }, 960 961 async activateAccount(token: string): Promise<void> { 962 await xrpc("com.atproto.server.activateAccount", { 963 method: "POST", 964 token, 965 }); 966 }, 967 968 async createPasskeyAccount(params: { 969 handle: string; 970 email?: string; 971 inviteCode?: string; 972 didType?: DidType; 973 did?: string; 974 signingKey?: string; 975 verificationChannel?: VerificationChannel; 976 discordId?: string; 977 telegramUsername?: string; 978 signalNumber?: string; 979 }, byodToken?: string): Promise<{ 980 did: string; 981 handle: string; 982 setupToken: string; 983 setupExpiresAt: string; 984 }> { 985 const url = `${API_BASE}/com.tranquil.account.createPasskeyAccount`; 986 const headers: Record<string, string> = { 987 "Content-Type": "application/json", 988 }; 989 if (byodToken) { 990 headers["Authorization"] = `Bearer ${byodToken}`; 991 } 992 const res = await fetch(url, { 993 method: "POST", 994 headers, 995 body: JSON.stringify(params), 996 }); 997 if (!res.ok) { 998 const err = await res.json().catch(() => ({ 999 error: "Unknown", 1000 message: res.statusText, 1001 })); 1002 throw new ApiError(res.status, err.error, err.message); 1003 } 1004 return res.json(); 1005 }, 1006 1007 async startPasskeyRegistrationForSetup( 1008 did: string, 1009 setupToken: string, 1010 friendlyName?: string, 1011 ): Promise<{ options: unknown }> { 1012 return xrpc("com.tranquil.account.startPasskeyRegistrationForSetup", { 1013 method: "POST", 1014 body: { did, setupToken, friendlyName }, 1015 }); 1016 }, 1017 1018 async completePasskeySetup( 1019 did: string, 1020 setupToken: string, 1021 passkeyCredential: unknown, 1022 passkeyFriendlyName?: string, 1023 ): Promise<{ 1024 did: string; 1025 handle: string; 1026 appPassword: string; 1027 appPasswordName: string; 1028 }> { 1029 return xrpc("com.tranquil.account.completePasskeySetup", { 1030 method: "POST", 1031 body: { did, setupToken, passkeyCredential, passkeyFriendlyName }, 1032 }); 1033 }, 1034 1035 async requestPasskeyRecovery(email: string): Promise<{ success: boolean }> { 1036 return xrpc("com.tranquil.account.requestPasskeyRecovery", { 1037 method: "POST", 1038 body: { email }, 1039 }); 1040 }, 1041 1042 async recoverPasskeyAccount( 1043 did: string, 1044 recoveryToken: string, 1045 newPassword: string, 1046 ): Promise<{ success: boolean }> { 1047 return xrpc("com.tranquil.account.recoverPasskeyAccount", { 1048 method: "POST", 1049 body: { did, recoveryToken, newPassword }, 1050 }); 1051 }, 1052 1053 async verifyMigrationEmail( 1054 token: string, 1055 email: string, 1056 ): Promise<{ success: boolean; did: string }> { 1057 return xrpc("com.atproto.server.verifyMigrationEmail", { 1058 method: "POST", 1059 body: { token, email }, 1060 }); 1061 }, 1062 1063 async resendMigrationVerification(email: string): Promise<{ sent: boolean }> { 1064 return xrpc("com.atproto.server.resendMigrationVerification", { 1065 method: "POST", 1066 body: { email }, 1067 }); 1068 }, 1069 1070 async verifyToken( 1071 token: string, 1072 identifier: string, 1073 accessToken?: string, 1074 ): Promise<{ 1075 success: boolean; 1076 did: string; 1077 purpose: string; 1078 channel: string; 1079 }> { 1080 return xrpc("com.tranquil.account.verifyToken", { 1081 method: "POST", 1082 body: { token, identifier }, 1083 token: accessToken, 1084 }); 1085 }, 1086 1087 async getDidDocument(token: string): Promise<DidDocument> { 1088 return xrpc("com.tranquil.account.getDidDocument", { token }); 1089 }, 1090 1091 async updateDidDocument( 1092 token: string, 1093 params: { 1094 verificationMethods?: VerificationMethod[]; 1095 alsoKnownAs?: string[]; 1096 serviceEndpoint?: string; 1097 }, 1098 ): Promise<{ success: boolean }> { 1099 return xrpc("com.tranquil.account.updateDidDocument", { 1100 method: "POST", 1101 token, 1102 body: params, 1103 }); 1104 }, 1105 1106 async deactivateAccount( 1107 token: string, 1108 deleteAfter?: string, 1109 migratingTo?: string, 1110 ): Promise<void> { 1111 await xrpc("com.atproto.server.deactivateAccount", { 1112 method: "POST", 1113 token, 1114 body: { deleteAfter, migratingTo }, 1115 }); 1116 }, 1117 1118 async getMigrationStatus(token: string): Promise<{ 1119 migratedToPds?: string; 1120 migratedAt?: string; 1121 forwardingEnabled: boolean; 1122 }> { 1123 return xrpc("com.tranquil.account.getMigrationStatus", { token }); 1124 }, 1125 1126 async updateMigrationForwarding( 1127 token: string, 1128 forwardingPds?: string, 1129 ): Promise<{ success: boolean }> { 1130 return xrpc("com.tranquil.account.updateMigrationForwarding", { 1131 method: "POST", 1132 token, 1133 body: { forwardingPds }, 1134 }); 1135 }, 1136 1137 async clearMigrationForwarding(token: string): Promise<{ success: boolean }> { 1138 return xrpc("com.tranquil.account.clearMigrationForwarding", { 1139 method: "POST", 1140 token, 1141 }); 1142 }, 1143};