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 createAccountWithServiceAuth( 209 serviceAuthToken: string, 210 params: { 211 did: string; 212 handle: string; 213 email: string; 214 password: string; 215 inviteCode?: string; 216 }, 217 ): Promise<Session> { 218 const url = `${API_BASE}/com.atproto.server.createAccount`; 219 const response = await fetch(url, { 220 method: "POST", 221 headers: { 222 "Content-Type": "application/json", 223 "Authorization": `Bearer ${serviceAuthToken}`, 224 }, 225 body: JSON.stringify({ 226 did: params.did, 227 handle: params.handle, 228 email: params.email, 229 password: params.password, 230 inviteCode: params.inviteCode, 231 }), 232 }); 233 const data = await response.json(); 234 if (!response.ok) { 235 throw new ApiError(response.status, data.error, data.message); 236 } 237 return data; 238 }, 239 240 confirmSignup( 241 did: string, 242 verificationCode: string, 243 ): Promise<ConfirmSignupResult> { 244 return xrpc("com.atproto.server.confirmSignup", { 245 method: "POST", 246 body: { did, verificationCode }, 247 }); 248 }, 249 250 resendVerification(did: string): Promise<{ success: boolean }> { 251 return xrpc("com.atproto.server.resendVerification", { 252 method: "POST", 253 body: { did }, 254 }); 255 }, 256 257 createSession(identifier: string, password: string): Promise<Session> { 258 return xrpc("com.atproto.server.createSession", { 259 method: "POST", 260 body: { identifier, password }, 261 }); 262 }, 263 264 checkEmailVerified(identifier: string): Promise<{ verified: boolean }> { 265 return xrpc("_checkEmailVerified", { 266 method: "POST", 267 body: { identifier }, 268 }); 269 }, 270 271 getSession(token: string): Promise<Session> { 272 return xrpc("com.atproto.server.getSession", { token }); 273 }, 274 275 refreshSession(refreshJwt: string): Promise<Session> { 276 return xrpc("com.atproto.server.refreshSession", { 277 method: "POST", 278 token: refreshJwt, 279 }); 280 }, 281 282 async deleteSession(token: string): Promise<void> { 283 await xrpc("com.atproto.server.deleteSession", { 284 method: "POST", 285 token, 286 }); 287 }, 288 289 listAppPasswords(token: string): Promise<{ passwords: AppPassword[] }> { 290 return xrpc("com.atproto.server.listAppPasswords", { token }); 291 }, 292 293 createAppPassword( 294 token: string, 295 name: string, 296 scopes?: string, 297 ): Promise< 298 { name: string; password: string; createdAt: string; scopes?: string } 299 > { 300 return xrpc("com.atproto.server.createAppPassword", { 301 method: "POST", 302 token, 303 body: { name, scopes }, 304 }); 305 }, 306 307 async revokeAppPassword(token: string, name: string): Promise<void> { 308 await xrpc("com.atproto.server.revokeAppPassword", { 309 method: "POST", 310 token, 311 body: { name }, 312 }); 313 }, 314 315 getAccountInviteCodes(token: string): Promise<{ codes: InviteCode[] }> { 316 return xrpc("com.atproto.server.getAccountInviteCodes", { token }); 317 }, 318 319 createInviteCode( 320 token: string, 321 useCount: number = 1, 322 ): Promise<{ code: string }> { 323 return xrpc("com.atproto.server.createInviteCode", { 324 method: "POST", 325 token, 326 body: { useCount }, 327 }); 328 }, 329 330 async requestPasswordReset(email: string): Promise<void> { 331 await xrpc("com.atproto.server.requestPasswordReset", { 332 method: "POST", 333 body: { email }, 334 }); 335 }, 336 337 async resetPassword(token: string, password: string): Promise<void> { 338 await xrpc("com.atproto.server.resetPassword", { 339 method: "POST", 340 body: { token, password }, 341 }); 342 }, 343 344 requestEmailUpdate( 345 token: string, 346 ): Promise<{ tokenRequired: boolean }> { 347 return xrpc("com.atproto.server.requestEmailUpdate", { 348 method: "POST", 349 token, 350 }); 351 }, 352 353 async updateEmail( 354 token: string, 355 email: string, 356 emailToken?: string, 357 ): Promise<void> { 358 await xrpc("com.atproto.server.updateEmail", { 359 method: "POST", 360 token, 361 body: { email, token: emailToken }, 362 }); 363 }, 364 365 async updateHandle(token: string, handle: string): Promise<void> { 366 await xrpc("com.atproto.identity.updateHandle", { 367 method: "POST", 368 token, 369 body: { handle }, 370 }); 371 }, 372 373 async requestAccountDelete(token: string): Promise<void> { 374 await xrpc("com.atproto.server.requestAccountDelete", { 375 method: "POST", 376 token, 377 }); 378 }, 379 380 async deleteAccount( 381 did: string, 382 password: string, 383 deleteToken: string, 384 ): Promise<void> { 385 await xrpc("com.atproto.server.deleteAccount", { 386 method: "POST", 387 body: { did, password, token: deleteToken }, 388 }); 389 }, 390 391 describeServer(): Promise<{ 392 availableUserDomains: string[]; 393 inviteCodeRequired: boolean; 394 links?: { privacyPolicy?: string; termsOfService?: string }; 395 version?: string; 396 availableCommsChannels?: string[]; 397 selfHostedDidWebEnabled?: boolean; 398 }> { 399 return xrpc("com.atproto.server.describeServer"); 400 }, 401 402 listRepos(limit?: number): Promise<{ 403 repos: Array<{ did: string; head: string; rev: string }>; 404 cursor?: string; 405 }> { 406 const params: Record<string, string> = {}; 407 if (limit) params.limit = String(limit); 408 return xrpc("com.atproto.sync.listRepos", { params }); 409 }, 410 411 getNotificationPrefs(token: string): Promise<{ 412 preferredChannel: string; 413 email: string; 414 discordId: string | null; 415 discordVerified: boolean; 416 telegramUsername: string | null; 417 telegramVerified: boolean; 418 signalNumber: string | null; 419 signalVerified: boolean; 420 }> { 421 return xrpc("_account.getNotificationPrefs", { token }); 422 }, 423 424 updateNotificationPrefs(token: string, prefs: { 425 preferredChannel?: string; 426 discordId?: string; 427 telegramUsername?: string; 428 signalNumber?: string; 429 }): Promise<{ success: boolean }> { 430 return xrpc("_account.updateNotificationPrefs", { 431 method: "POST", 432 token, 433 body: prefs, 434 }); 435 }, 436 437 confirmChannelVerification( 438 token: string, 439 channel: string, 440 identifier: string, 441 code: string, 442 ): Promise<{ success: boolean }> { 443 return xrpc("_account.confirmChannelVerification", { 444 method: "POST", 445 token, 446 body: { channel, identifier, code }, 447 }); 448 }, 449 450 getNotificationHistory(token: string): Promise<{ 451 notifications: Array<{ 452 createdAt: string; 453 channel: string; 454 notificationType: string; 455 status: string; 456 subject: string | null; 457 body: string; 458 }>; 459 }> { 460 return xrpc("_account.getNotificationHistory", { token }); 461 }, 462 463 getServerStats(token: string): Promise<{ 464 userCount: number; 465 repoCount: number; 466 recordCount: number; 467 blobStorageBytes: number; 468 }> { 469 return xrpc("_admin.getServerStats", { token }); 470 }, 471 472 getServerConfig(): Promise<{ 473 serverName: string; 474 primaryColor: string | null; 475 primaryColorDark: string | null; 476 secondaryColor: string | null; 477 secondaryColorDark: string | null; 478 logoCid: string | null; 479 }> { 480 return xrpc("_server.getConfig"); 481 }, 482 483 updateServerConfig( 484 token: string, 485 config: { 486 serverName?: string; 487 primaryColor?: string; 488 primaryColorDark?: string; 489 secondaryColor?: string; 490 secondaryColorDark?: string; 491 logoCid?: string; 492 }, 493 ): Promise<{ success: boolean }> { 494 return xrpc("_admin.updateServerConfig", { 495 method: "POST", 496 token, 497 body: config, 498 }); 499 }, 500 501 async uploadBlob( 502 token: string, 503 file: File, 504 ): Promise< 505 { 506 blob: { 507 $type: string; 508 ref: { $link: string }; 509 mimeType: string; 510 size: number; 511 }; 512 } 513 > { 514 const res = await fetch("/xrpc/com.atproto.repo.uploadBlob", { 515 method: "POST", 516 headers: { 517 "Authorization": `Bearer ${token}`, 518 "Content-Type": file.type, 519 }, 520 body: file, 521 }); 522 if (!res.ok) { 523 const err = await res.json().catch(() => ({ 524 error: "Unknown", 525 message: res.statusText, 526 })); 527 throw new ApiError(res.status, err.error, err.message); 528 } 529 return res.json(); 530 }, 531 532 async changePassword( 533 token: string, 534 currentPassword: string, 535 newPassword: string, 536 ): Promise<void> { 537 await xrpc("_account.changePassword", { 538 method: "POST", 539 token, 540 body: { currentPassword, newPassword }, 541 }); 542 }, 543 544 removePassword(token: string): Promise<{ success: boolean }> { 545 return xrpc("_account.removePassword", { 546 method: "POST", 547 token, 548 }); 549 }, 550 551 getPasswordStatus(token: string): Promise<{ hasPassword: boolean }> { 552 return xrpc("_account.getPasswordStatus", { token }); 553 }, 554 555 getLegacyLoginPreference( 556 token: string, 557 ): Promise<{ allowLegacyLogin: boolean; hasMfa: boolean }> { 558 return xrpc("_account.getLegacyLoginPreference", { token }); 559 }, 560 561 updateLegacyLoginPreference( 562 token: string, 563 allowLegacyLogin: boolean, 564 ): Promise<{ allowLegacyLogin: boolean }> { 565 return xrpc("_account.updateLegacyLoginPreference", { 566 method: "POST", 567 token, 568 body: { allowLegacyLogin }, 569 }); 570 }, 571 572 updateLocale( 573 token: string, 574 preferredLocale: string, 575 ): Promise<{ preferredLocale: string }> { 576 return xrpc("_account.updateLocale", { 577 method: "POST", 578 token, 579 body: { preferredLocale }, 580 }); 581 }, 582 583 listSessions(token: string): Promise<{ 584 sessions: Array<{ 585 id: string; 586 sessionType: string; 587 clientName: string | null; 588 createdAt: string; 589 expiresAt: string; 590 isCurrent: boolean; 591 }>; 592 }> { 593 return xrpc("_account.listSessions", { token }); 594 }, 595 596 async revokeSession(token: string, sessionId: string): Promise<void> { 597 await xrpc("_account.revokeSession", { 598 method: "POST", 599 token, 600 body: { sessionId }, 601 }); 602 }, 603 604 revokeAllSessions(token: string): Promise<{ revokedCount: number }> { 605 return xrpc("_account.revokeAllSessions", { 606 method: "POST", 607 token, 608 }); 609 }, 610 611 searchAccounts(token: string, options?: { 612 handle?: string; 613 cursor?: string; 614 limit?: number; 615 }): Promise<{ 616 cursor?: string; 617 accounts: Array<{ 618 did: string; 619 handle: string; 620 email?: string; 621 indexedAt: string; 622 emailConfirmedAt?: string; 623 deactivatedAt?: string; 624 }>; 625 }> { 626 const params: Record<string, string> = {}; 627 if (options?.handle) params.handle = options.handle; 628 if (options?.cursor) params.cursor = options.cursor; 629 if (options?.limit) params.limit = String(options.limit); 630 return xrpc("com.atproto.admin.searchAccounts", { token, params }); 631 }, 632 633 getInviteCodes(token: string, options?: { 634 sort?: "recent" | "usage"; 635 cursor?: string; 636 limit?: number; 637 }): Promise<{ 638 cursor?: string; 639 codes: Array<{ 640 code: string; 641 available: number; 642 disabled: boolean; 643 forAccount: string; 644 createdBy: string; 645 createdAt: string; 646 uses: Array<{ usedBy: string; usedAt: string }>; 647 }>; 648 }> { 649 const params: Record<string, string> = {}; 650 if (options?.sort) params.sort = options.sort; 651 if (options?.cursor) params.cursor = options.cursor; 652 if (options?.limit) params.limit = String(options.limit); 653 return xrpc("com.atproto.admin.getInviteCodes", { token, params }); 654 }, 655 656 async disableInviteCodes( 657 token: string, 658 codes?: string[], 659 accounts?: string[], 660 ): Promise<void> { 661 await xrpc("com.atproto.admin.disableInviteCodes", { 662 method: "POST", 663 token, 664 body: { codes, accounts }, 665 }); 666 }, 667 668 getAccountInfo(token: string, did: string): Promise<{ 669 did: string; 670 handle: string; 671 email?: string; 672 indexedAt: string; 673 emailConfirmedAt?: string; 674 invitesDisabled?: boolean; 675 deactivatedAt?: string; 676 }> { 677 return xrpc("com.atproto.admin.getAccountInfo", { token, params: { did } }); 678 }, 679 680 async disableAccountInvites(token: string, account: string): Promise<void> { 681 await xrpc("com.atproto.admin.disableAccountInvites", { 682 method: "POST", 683 token, 684 body: { account }, 685 }); 686 }, 687 688 async enableAccountInvites(token: string, account: string): Promise<void> { 689 await xrpc("com.atproto.admin.enableAccountInvites", { 690 method: "POST", 691 token, 692 body: { account }, 693 }); 694 }, 695 696 async adminDeleteAccount(token: string, did: string): Promise<void> { 697 await xrpc("com.atproto.admin.deleteAccount", { 698 method: "POST", 699 token, 700 body: { did }, 701 }); 702 }, 703 704 describeRepo(token: string, repo: string): Promise<{ 705 handle: string; 706 did: string; 707 didDoc: unknown; 708 collections: string[]; 709 handleIsCorrect: boolean; 710 }> { 711 return xrpc("com.atproto.repo.describeRepo", { 712 token, 713 params: { repo }, 714 }); 715 }, 716 717 listRecords(token: string, repo: string, collection: string, options?: { 718 limit?: number; 719 cursor?: string; 720 reverse?: boolean; 721 }): Promise<{ 722 records: Array<{ uri: string; cid: string; value: unknown }>; 723 cursor?: string; 724 }> { 725 const params: Record<string, string> = { repo, collection }; 726 if (options?.limit) params.limit = String(options.limit); 727 if (options?.cursor) params.cursor = options.cursor; 728 if (options?.reverse) params.reverse = "true"; 729 return xrpc("com.atproto.repo.listRecords", { token, params }); 730 }, 731 732 getRecord( 733 token: string, 734 repo: string, 735 collection: string, 736 rkey: string, 737 ): Promise<{ 738 uri: string; 739 cid: string; 740 value: unknown; 741 }> { 742 return xrpc("com.atproto.repo.getRecord", { 743 token, 744 params: { repo, collection, rkey }, 745 }); 746 }, 747 748 createRecord( 749 token: string, 750 repo: string, 751 collection: string, 752 record: unknown, 753 rkey?: string, 754 ): Promise<{ 755 uri: string; 756 cid: string; 757 }> { 758 return xrpc("com.atproto.repo.createRecord", { 759 method: "POST", 760 token, 761 body: { repo, collection, record, rkey }, 762 }); 763 }, 764 765 putRecord( 766 token: string, 767 repo: string, 768 collection: string, 769 rkey: string, 770 record: unknown, 771 ): Promise<{ 772 uri: string; 773 cid: string; 774 }> { 775 return xrpc("com.atproto.repo.putRecord", { 776 method: "POST", 777 token, 778 body: { repo, collection, rkey, record }, 779 }); 780 }, 781 782 async deleteRecord( 783 token: string, 784 repo: string, 785 collection: string, 786 rkey: string, 787 ): Promise<void> { 788 await xrpc("com.atproto.repo.deleteRecord", { 789 method: "POST", 790 token, 791 body: { repo, collection, rkey }, 792 }); 793 }, 794 795 getTotpStatus( 796 token: string, 797 ): Promise<{ enabled: boolean; hasBackupCodes: boolean }> { 798 return xrpc("com.atproto.server.getTotpStatus", { token }); 799 }, 800 801 createTotpSecret( 802 token: string, 803 ): Promise<{ uri: string; qrBase64: string }> { 804 return xrpc("com.atproto.server.createTotpSecret", { 805 method: "POST", 806 token, 807 }); 808 }, 809 810 enableTotp( 811 token: string, 812 code: string, 813 ): Promise<{ success: boolean; backupCodes: string[] }> { 814 return xrpc("com.atproto.server.enableTotp", { 815 method: "POST", 816 token, 817 body: { code }, 818 }); 819 }, 820 821 disableTotp( 822 token: string, 823 password: string, 824 code: string, 825 ): Promise<{ success: boolean }> { 826 return xrpc("com.atproto.server.disableTotp", { 827 method: "POST", 828 token, 829 body: { password, code }, 830 }); 831 }, 832 833 regenerateBackupCodes( 834 token: string, 835 password: string, 836 code: string, 837 ): Promise<{ backupCodes: string[] }> { 838 return xrpc("com.atproto.server.regenerateBackupCodes", { 839 method: "POST", 840 token, 841 body: { password, code }, 842 }); 843 }, 844 845 startPasskeyRegistration( 846 token: string, 847 friendlyName?: string, 848 ): Promise<{ options: unknown }> { 849 return xrpc("com.atproto.server.startPasskeyRegistration", { 850 method: "POST", 851 token, 852 body: { friendlyName }, 853 }); 854 }, 855 856 finishPasskeyRegistration( 857 token: string, 858 credential: unknown, 859 friendlyName?: string, 860 ): Promise<{ id: string; credentialId: string }> { 861 return xrpc("com.atproto.server.finishPasskeyRegistration", { 862 method: "POST", 863 token, 864 body: { credential, friendlyName }, 865 }); 866 }, 867 868 listPasskeys(token: string): Promise<{ 869 passkeys: Array<{ 870 id: string; 871 credentialId: string; 872 friendlyName: string | null; 873 createdAt: string; 874 lastUsed: string | null; 875 }>; 876 }> { 877 return xrpc("com.atproto.server.listPasskeys", { token }); 878 }, 879 880 async deletePasskey(token: string, id: string): Promise<void> { 881 await xrpc("com.atproto.server.deletePasskey", { 882 method: "POST", 883 token, 884 body: { id }, 885 }); 886 }, 887 888 async updatePasskey( 889 token: string, 890 id: string, 891 friendlyName: string, 892 ): Promise<void> { 893 await xrpc("com.atproto.server.updatePasskey", { 894 method: "POST", 895 token, 896 body: { id, friendlyName }, 897 }); 898 }, 899 900 listTrustedDevices(token: string): Promise<{ 901 devices: Array<{ 902 id: string; 903 userAgent: string | null; 904 friendlyName: string | null; 905 trustedAt: string | null; 906 trustedUntil: string | null; 907 lastSeenAt: string; 908 }>; 909 }> { 910 return xrpc("_account.listTrustedDevices", { token }); 911 }, 912 913 revokeTrustedDevice( 914 token: string, 915 deviceId: string, 916 ): Promise<{ success: boolean }> { 917 return xrpc("_account.revokeTrustedDevice", { 918 method: "POST", 919 token, 920 body: { deviceId }, 921 }); 922 }, 923 924 updateTrustedDevice( 925 token: string, 926 deviceId: string, 927 friendlyName: string, 928 ): Promise<{ success: boolean }> { 929 return xrpc("_account.updateTrustedDevice", { 930 method: "POST", 931 token, 932 body: { deviceId, friendlyName }, 933 }); 934 }, 935 936 getReauthStatus(token: string): Promise<{ 937 requiresReauth: boolean; 938 lastReauthAt: string | null; 939 availableMethods: string[]; 940 }> { 941 return xrpc("_account.getReauthStatus", { token }); 942 }, 943 944 reauthPassword( 945 token: string, 946 password: string, 947 ): Promise<{ success: boolean; reauthAt: string }> { 948 return xrpc("_account.reauthPassword", { 949 method: "POST", 950 token, 951 body: { password }, 952 }); 953 }, 954 955 reauthTotp( 956 token: string, 957 code: string, 958 ): Promise<{ success: boolean; reauthAt: string }> { 959 return xrpc("_account.reauthTotp", { 960 method: "POST", 961 token, 962 body: { code }, 963 }); 964 }, 965 966 reauthPasskeyStart(token: string): Promise<{ options: unknown }> { 967 return xrpc("_account.reauthPasskeyStart", { 968 method: "POST", 969 token, 970 }); 971 }, 972 973 reauthPasskeyFinish( 974 token: string, 975 credential: unknown, 976 ): Promise<{ success: boolean; reauthAt: string }> { 977 return xrpc("_account.reauthPasskeyFinish", { 978 method: "POST", 979 token, 980 body: { credential }, 981 }); 982 }, 983 984 reserveSigningKey(did?: string): Promise<{ signingKey: string }> { 985 return xrpc("com.atproto.server.reserveSigningKey", { 986 method: "POST", 987 body: { did }, 988 }); 989 }, 990 991 getRecommendedDidCredentials(token: string): Promise<{ 992 rotationKeys?: string[]; 993 alsoKnownAs?: string[]; 994 verificationMethods?: { atproto?: string }; 995 services?: { atproto_pds?: { type: string; endpoint: string } }; 996 }> { 997 return xrpc("com.atproto.identity.getRecommendedDidCredentials", { token }); 998 }, 999 1000 async activateAccount(token: string): Promise<void> { 1001 await xrpc("com.atproto.server.activateAccount", { 1002 method: "POST", 1003 token, 1004 }); 1005 }, 1006 1007 async createPasskeyAccount(params: { 1008 handle: string; 1009 email?: string; 1010 inviteCode?: string; 1011 didType?: DidType; 1012 did?: string; 1013 signingKey?: string; 1014 verificationChannel?: VerificationChannel; 1015 discordId?: string; 1016 telegramUsername?: string; 1017 signalNumber?: string; 1018 }, byodToken?: string): Promise<{ 1019 did: string; 1020 handle: string; 1021 setupToken: string; 1022 setupExpiresAt: string; 1023 }> { 1024 const url = `${API_BASE}/_account.createPasskeyAccount`; 1025 const headers: Record<string, string> = { 1026 "Content-Type": "application/json", 1027 }; 1028 if (byodToken) { 1029 headers["Authorization"] = `Bearer ${byodToken}`; 1030 } 1031 const res = await fetch(url, { 1032 method: "POST", 1033 headers, 1034 body: JSON.stringify(params), 1035 }); 1036 if (!res.ok) { 1037 const err = await res.json().catch(() => ({ 1038 error: "Unknown", 1039 message: res.statusText, 1040 })); 1041 throw new ApiError(res.status, err.error, err.message); 1042 } 1043 return res.json(); 1044 }, 1045 1046 startPasskeyRegistrationForSetup( 1047 did: string, 1048 setupToken: string, 1049 friendlyName?: string, 1050 ): Promise<{ options: unknown }> { 1051 return xrpc("_account.startPasskeyRegistrationForSetup", { 1052 method: "POST", 1053 body: { did, setupToken, friendlyName }, 1054 }); 1055 }, 1056 1057 completePasskeySetup( 1058 did: string, 1059 setupToken: string, 1060 passkeyCredential: unknown, 1061 passkeyFriendlyName?: string, 1062 ): Promise<{ 1063 did: string; 1064 handle: string; 1065 appPassword: string; 1066 appPasswordName: string; 1067 }> { 1068 return xrpc("_account.completePasskeySetup", { 1069 method: "POST", 1070 body: { did, setupToken, passkeyCredential, passkeyFriendlyName }, 1071 }); 1072 }, 1073 1074 requestPasskeyRecovery(email: string): Promise<{ success: boolean }> { 1075 return xrpc("_account.requestPasskeyRecovery", { 1076 method: "POST", 1077 body: { email }, 1078 }); 1079 }, 1080 1081 recoverPasskeyAccount( 1082 did: string, 1083 recoveryToken: string, 1084 newPassword: string, 1085 ): Promise<{ success: boolean }> { 1086 return xrpc("_account.recoverPasskeyAccount", { 1087 method: "POST", 1088 body: { did, recoveryToken, newPassword }, 1089 }); 1090 }, 1091 1092 verifyMigrationEmail( 1093 token: string, 1094 email: string, 1095 ): Promise<{ success: boolean; did: string }> { 1096 return xrpc("com.atproto.server.verifyMigrationEmail", { 1097 method: "POST", 1098 body: { token, email }, 1099 }); 1100 }, 1101 1102 resendMigrationVerification(email: string): Promise<{ sent: boolean }> { 1103 return xrpc("com.atproto.server.resendMigrationVerification", { 1104 method: "POST", 1105 body: { email }, 1106 }); 1107 }, 1108 1109 verifyToken( 1110 token: string, 1111 identifier: string, 1112 accessToken?: string, 1113 ): Promise<{ 1114 success: boolean; 1115 did: string; 1116 purpose: string; 1117 channel: string; 1118 }> { 1119 return xrpc("_account.verifyToken", { 1120 method: "POST", 1121 body: { token, identifier }, 1122 token: accessToken, 1123 }); 1124 }, 1125 1126 getDidDocument(token: string): Promise<DidDocument> { 1127 return xrpc("_account.getDidDocument", { token }); 1128 }, 1129 1130 updateDidDocument( 1131 token: string, 1132 params: { 1133 verificationMethods?: VerificationMethod[]; 1134 alsoKnownAs?: string[]; 1135 serviceEndpoint?: string; 1136 }, 1137 ): Promise<{ success: boolean }> { 1138 return xrpc("_account.updateDidDocument", { 1139 method: "POST", 1140 token, 1141 body: params, 1142 }); 1143 }, 1144 1145 async deactivateAccount( 1146 token: string, 1147 deleteAfter?: string, 1148 ): Promise<void> { 1149 await xrpc("com.atproto.server.deactivateAccount", { 1150 method: "POST", 1151 token, 1152 body: { deleteAfter }, 1153 }); 1154 }, 1155 1156 async getRepo(token: string, did: string): Promise<ArrayBuffer> { 1157 const url = `${API_BASE}/com.atproto.sync.getRepo?did=${ 1158 encodeURIComponent(did) 1159 }`; 1160 const res = await fetch(url, { 1161 headers: { Authorization: `Bearer ${token}` }, 1162 }); 1163 if (!res.ok) { 1164 const err = await res.json().catch(() => ({ 1165 error: "Unknown", 1166 message: res.statusText, 1167 })); 1168 throw new ApiError(res.status, err.error, err.message); 1169 } 1170 return res.arrayBuffer(); 1171 }, 1172 1173 listBackups(token: string): Promise<{ 1174 backups: Array<{ 1175 id: string; 1176 repoRev: string; 1177 repoRootCid: string; 1178 blockCount: number; 1179 sizeBytes: number; 1180 createdAt: string; 1181 }>; 1182 backupEnabled: boolean; 1183 }> { 1184 return xrpc("_backup.listBackups", { token }); 1185 }, 1186 1187 async getBackup(token: string, id: string): Promise<Blob> { 1188 const url = `${API_BASE}/_backup.getBackup?id=${encodeURIComponent(id)}`; 1189 const res = await fetch(url, { 1190 headers: { Authorization: `Bearer ${token}` }, 1191 }); 1192 if (!res.ok) { 1193 const err = await res.json().catch(() => ({ 1194 error: "Unknown", 1195 message: res.statusText, 1196 })); 1197 throw new ApiError(res.status, err.error, err.message); 1198 } 1199 return res.blob(); 1200 }, 1201 1202 createBackup(token: string): Promise<{ 1203 id: string; 1204 repoRev: string; 1205 sizeBytes: number; 1206 blockCount: number; 1207 }> { 1208 return xrpc("_backup.createBackup", { 1209 method: "POST", 1210 token, 1211 }); 1212 }, 1213 1214 async deleteBackup(token: string, id: string): Promise<void> { 1215 await xrpc("_backup.deleteBackup", { 1216 method: "POST", 1217 token, 1218 params: { id }, 1219 }); 1220 }, 1221 1222 setBackupEnabled( 1223 token: string, 1224 enabled: boolean, 1225 ): Promise<{ enabled: boolean }> { 1226 return xrpc("_backup.setEnabled", { 1227 method: "POST", 1228 token, 1229 body: { enabled }, 1230 }); 1231 }, 1232 1233 async importRepo(token: string, car: Uint8Array): Promise<void> { 1234 const url = `${API_BASE}/com.atproto.repo.importRepo`; 1235 const res = await fetch(url, { 1236 method: "POST", 1237 headers: { 1238 Authorization: `Bearer ${token}`, 1239 "Content-Type": "application/vnd.ipld.car", 1240 }, 1241 body: car, 1242 }); 1243 if (!res.ok) { 1244 const err = await res.json().catch(() => ({ 1245 error: "Unknown", 1246 message: res.statusText, 1247 })); 1248 throw new ApiError(res.status, err.error, err.message); 1249 } 1250 }, 1251};