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