Our Personal Data Server from scratch!
at fix/code-quality-in-general 1899 lines 49 kB view raw
1import { err, ok, type Result } from "./types/result.ts"; 2import type { 3 AccessToken, 4 Did, 5 EmailAddress, 6 Handle, 7 Nsid, 8 RefreshToken, 9 Rkey, 10} from "./types/branded.ts"; 11import { 12 unsafeAsAccessToken, 13 unsafeAsDid, 14 unsafeAsEmail, 15 unsafeAsHandle, 16 unsafeAsISODate, 17 unsafeAsRefreshToken, 18} from "./types/branded.ts"; 19import { 20 createDPoPProofForRequest, 21 getDPoPNonce, 22 setDPoPNonce, 23} from "./oauth.ts"; 24import type { 25 AccountInfo, 26 ApiErrorCode, 27 AppPassword, 28 CompletePasskeySetupResponse, 29 ConfirmSignupResult, 30 CreateAccountParams, 31 CreateAccountResult, 32 CreateBackupResponse, 33 CreatedAppPassword, 34 CreateRecordResponse, 35 DidDocument, 36 DidType, 37 EmailUpdateResponse, 38 EnableTotpResponse, 39 FinishPasskeyRegistrationResponse, 40 GetInviteCodesResponse, 41 InviteCodeInfo, 42 LegacyLoginPreference, 43 ListBackupsResponse, 44 ListPasskeysResponse, 45 ListRecordsResponse, 46 ListReposResponse, 47 ListSessionsResponse, 48 ListTrustedDevicesResponse, 49 NotificationHistoryResponse, 50 NotificationPrefs, 51 PasskeyAccountCreateResponse, 52 PasswordStatus, 53 ReauthPasskeyStartResponse, 54 ReauthResponse, 55 ReauthStatus, 56 RecommendedDidCredentials, 57 RecordResponse, 58 RegenerateBackupCodesResponse, 59 RepoDescription, 60 ResendMigrationVerificationResponse, 61 ReserveSigningKeyResponse, 62 SearchAccountsResponse, 63 ServerConfig, 64 ServerDescription, 65 ServerStats, 66 Session, 67 SetBackupEnabledResponse, 68 StartPasskeyRegistrationResponse, 69 SuccessResponse, 70 TotpSecret, 71 TotpStatus, 72 UpdateLegacyLoginResponse, 73 UpdateLocaleResponse, 74 UploadBlobResponse, 75 VerificationChannel, 76 VerifyMigrationEmailResponse, 77 VerifyTokenResponse, 78} from "./types/api.ts"; 79 80const API_BASE = "/xrpc"; 81 82export class ApiError extends Error { 83 public did?: Did; 84 public reauthMethods?: string[]; 85 constructor( 86 public status: number, 87 public error: ApiErrorCode, 88 message: string, 89 did?: string, 90 reauthMethods?: string[], 91 ) { 92 super(message); 93 this.name = "ApiError"; 94 this.did = did ? unsafeAsDid(did) : undefined; 95 this.reauthMethods = reauthMethods; 96 } 97} 98 99let tokenRefreshCallback: (() => Promise<AccessToken | null>) | null = null; 100 101export function setTokenRefreshCallback( 102 callback: () => Promise<AccessToken | null>, 103) { 104 tokenRefreshCallback = callback; 105} 106 107interface AuthenticatedFetchOptions { 108 method?: "GET" | "POST"; 109 token: AccessToken | RefreshToken; 110 headers?: Record<string, string>; 111 body?: BodyInit; 112} 113 114async function authenticatedFetch( 115 url: string, 116 options: AuthenticatedFetchOptions, 117): Promise<Response> { 118 const { method = "GET", token, headers = {}, body } = options; 119 const fullUrl = url.startsWith("http") 120 ? url 121 : `${globalThis.location.origin}${url}`; 122 const dpopProof = await createDPoPProofForRequest(method, fullUrl, token); 123 const res = await fetch(url, { 124 method, 125 headers: { 126 ...headers, 127 Authorization: `DPoP ${token}`, 128 DPoP: dpopProof, 129 }, 130 body, 131 }); 132 const dpopNonce = res.headers.get("DPoP-Nonce"); 133 if (dpopNonce) { 134 setDPoPNonce(dpopNonce); 135 } 136 return res; 137} 138 139interface XrpcOptions { 140 method?: "GET" | "POST"; 141 params?: Record<string, string>; 142 body?: unknown; 143 token?: AccessToken | RefreshToken; 144 skipRetry?: boolean; 145 skipDpopRetry?: boolean; 146} 147 148async function xrpc<T>(method: string, options?: XrpcOptions): Promise<T> { 149 const { 150 method: httpMethod = "GET", 151 params, 152 body, 153 token, 154 skipRetry, 155 skipDpopRetry, 156 } = options ?? {}; 157 let url = `${API_BASE}/${method}`; 158 if (params) { 159 const searchParams = new URLSearchParams(params); 160 url += `?${searchParams}`; 161 } 162 const headers: Record<string, string> = {}; 163 if (body) { 164 headers["Content-Type"] = "application/json"; 165 } 166 const res = token 167 ? await authenticatedFetch(url, { 168 method: httpMethod, 169 token, 170 headers, 171 body: body ? JSON.stringify(body) : undefined, 172 }) 173 : await fetch(url, { 174 method: httpMethod, 175 headers, 176 body: body ? JSON.stringify(body) : undefined, 177 }); 178 if (!res.ok) { 179 const errData = await res.json().catch(() => ({ 180 error: "Unknown", 181 message: res.statusText, 182 })); 183 if ( 184 res.status === 401 && 185 errData.error === "use_dpop_nonce" && 186 token && 187 !skipDpopRetry && 188 getDPoPNonce() 189 ) { 190 return xrpc(method, { ...options, skipDpopRetry: true }); 191 } 192 if ( 193 res.status === 401 && 194 (errData.error === "AuthenticationFailed" || 195 errData.error === "ExpiredToken" || 196 errData.error === "OAuthExpiredToken") && 197 token && 198 tokenRefreshCallback && 199 !skipRetry 200 ) { 201 const newToken = await tokenRefreshCallback(); 202 if (newToken && newToken !== token) { 203 return xrpc(method, { ...options, token: newToken, skipRetry: true }); 204 } 205 } 206 const message = res.status === 429 207 ? (errData.message || "Too many requests. Please try again later.") 208 : errData.message; 209 throw new ApiError( 210 res.status, 211 errData.error as ApiErrorCode, 212 message, 213 errData.did, 214 errData.reauthMethods, 215 ); 216 } 217 return res.json(); 218} 219 220async function xrpcResult<T>( 221 method: string, 222 options?: XrpcOptions, 223): Promise<Result<T, ApiError>> { 224 try { 225 const value = await xrpc<T>(method, options); 226 return ok(value); 227 } catch (e) { 228 if (e instanceof ApiError) { 229 return err(e); 230 } 231 return err( 232 new ApiError(0, "Unknown", e instanceof Error ? e.message : String(e)), 233 ); 234 } 235} 236 237export interface VerificationMethod { 238 id: string; 239 type: string; 240 publicKeyMultibase: string; 241} 242 243export type { AppPassword, DidDocument, InviteCodeInfo as InviteCode, Session }; 244export type { 245 ConfirmSignupResult, 246 CreateAccountParams, 247 CreateAccountResult, 248 DidType, 249 VerificationChannel, 250}; 251 252function castSession(raw: unknown): Session { 253 const s = raw as Record<string, unknown>; 254 return { 255 did: unsafeAsDid(s.did as string), 256 handle: unsafeAsHandle(s.handle as string), 257 email: s.email ? unsafeAsEmail(s.email as string) : undefined, 258 emailConfirmed: s.emailConfirmed as boolean | undefined, 259 preferredChannel: s.preferredChannel as VerificationChannel | undefined, 260 preferredChannelVerified: s.preferredChannelVerified as boolean | undefined, 261 isAdmin: s.isAdmin as boolean | undefined, 262 active: s.active as boolean | undefined, 263 status: s.status as Session["status"], 264 migratedToPds: s.migratedToPds as string | undefined, 265 migratedAt: s.migratedAt 266 ? unsafeAsISODate(s.migratedAt as string) 267 : undefined, 268 accessJwt: unsafeAsAccessToken(s.accessJwt as string), 269 refreshJwt: unsafeAsRefreshToken(s.refreshJwt as string), 270 }; 271} 272 273export const api = { 274 async createAccount( 275 params: CreateAccountParams, 276 byodToken?: string, 277 ): Promise<CreateAccountResult> { 278 const url = `${API_BASE}/com.atproto.server.createAccount`; 279 const headers: Record<string, string> = { 280 "Content-Type": "application/json", 281 }; 282 if (byodToken) { 283 headers["Authorization"] = `Bearer ${byodToken}`; 284 } 285 const response = await fetch(url, { 286 method: "POST", 287 headers, 288 body: JSON.stringify({ 289 handle: params.handle, 290 email: params.email, 291 password: params.password, 292 inviteCode: params.inviteCode, 293 didType: params.didType, 294 did: params.did, 295 signingKey: params.signingKey, 296 verificationChannel: params.verificationChannel, 297 discordId: params.discordId, 298 telegramUsername: params.telegramUsername, 299 signalNumber: params.signalNumber, 300 }), 301 }); 302 const data = await response.json(); 303 if (!response.ok) { 304 throw new ApiError(response.status, data.error, data.message); 305 } 306 return data; 307 }, 308 309 async createAccountWithServiceAuth( 310 serviceAuthToken: string, 311 params: { 312 did: Did; 313 handle: Handle; 314 email: EmailAddress; 315 password: string; 316 inviteCode?: string; 317 }, 318 ): Promise<Session> { 319 const url = `${API_BASE}/com.atproto.server.createAccount`; 320 const response = await fetch(url, { 321 method: "POST", 322 headers: { 323 "Content-Type": "application/json", 324 "Authorization": `Bearer ${serviceAuthToken}`, 325 }, 326 body: JSON.stringify({ 327 did: params.did, 328 handle: params.handle, 329 email: params.email, 330 password: params.password, 331 inviteCode: params.inviteCode, 332 }), 333 }); 334 const data = await response.json(); 335 if (!response.ok) { 336 throw new ApiError(response.status, data.error, data.message); 337 } 338 return castSession(data); 339 }, 340 341 confirmSignup( 342 did: Did, 343 verificationCode: string, 344 ): Promise<ConfirmSignupResult> { 345 return xrpc("com.atproto.server.confirmSignup", { 346 method: "POST", 347 body: { did, verificationCode }, 348 }); 349 }, 350 351 resendVerification(did: Did): Promise<{ success: boolean }> { 352 return xrpc("com.atproto.server.resendVerification", { 353 method: "POST", 354 body: { did }, 355 }); 356 }, 357 358 async createSession(identifier: string, password: string): Promise<Session> { 359 const raw = await xrpc<unknown>("com.atproto.server.createSession", { 360 method: "POST", 361 body: { identifier, password }, 362 }); 363 return castSession(raw); 364 }, 365 366 checkEmailVerified(identifier: string): Promise<{ verified: boolean }> { 367 return xrpc("_checkEmailVerified", { 368 method: "POST", 369 body: { identifier }, 370 }); 371 }, 372 373 checkEmailInUse(email: string): Promise<{ inUse: boolean }> { 374 return xrpc("_account.checkEmailInUse", { 375 method: "POST", 376 body: { email }, 377 }); 378 }, 379 380 checkCommsChannelInUse( 381 channel: "email" | "discord" | "telegram" | "signal", 382 identifier: string, 383 ): Promise<{ inUse: boolean }> { 384 return xrpc("_account.checkCommsChannelInUse", { 385 method: "POST", 386 body: { channel, identifier }, 387 }); 388 }, 389 390 async getSession(token: AccessToken): Promise<Session> { 391 const raw = await xrpc<unknown>("com.atproto.server.getSession", { token }); 392 return castSession(raw); 393 }, 394 395 async refreshSession(refreshJwt: RefreshToken): Promise<Session> { 396 const raw = await xrpc<unknown>("com.atproto.server.refreshSession", { 397 method: "POST", 398 token: refreshJwt, 399 }); 400 return castSession(raw); 401 }, 402 403 async deleteSession(token: AccessToken): Promise<void> { 404 await xrpc("com.atproto.server.deleteSession", { 405 method: "POST", 406 token, 407 }); 408 }, 409 410 listAppPasswords(token: AccessToken): Promise<{ passwords: AppPassword[] }> { 411 return xrpc("com.atproto.server.listAppPasswords", { token }); 412 }, 413 414 createAppPassword( 415 token: AccessToken, 416 name: string, 417 scopes?: string, 418 ): Promise<CreatedAppPassword> { 419 return xrpc("com.atproto.server.createAppPassword", { 420 method: "POST", 421 token, 422 body: { name, scopes }, 423 }); 424 }, 425 426 async revokeAppPassword(token: AccessToken, name: string): Promise<void> { 427 await xrpc("com.atproto.server.revokeAppPassword", { 428 method: "POST", 429 token, 430 body: { name }, 431 }); 432 }, 433 434 getAccountInviteCodes( 435 token: AccessToken, 436 ): Promise<{ codes: InviteCodeInfo[] }> { 437 return xrpc("com.atproto.server.getAccountInviteCodes", { token }); 438 }, 439 440 createInviteCode( 441 token: AccessToken, 442 useCount: number = 1, 443 ): Promise<{ code: string }> { 444 return xrpc("com.atproto.server.createInviteCode", { 445 method: "POST", 446 token, 447 body: { useCount }, 448 }); 449 }, 450 451 async requestPasswordReset(email: EmailAddress): Promise<void> { 452 await xrpc("com.atproto.server.requestPasswordReset", { 453 method: "POST", 454 body: { email }, 455 }); 456 }, 457 458 async resetPassword(token: string, password: string): Promise<void> { 459 await xrpc("com.atproto.server.resetPassword", { 460 method: "POST", 461 body: { token, password }, 462 }); 463 }, 464 465 requestEmailUpdate( 466 token: AccessToken, 467 newEmail?: string, 468 ): Promise<EmailUpdateResponse> { 469 return xrpc("com.atproto.server.requestEmailUpdate", { 470 method: "POST", 471 token, 472 body: newEmail ? { newEmail } : undefined, 473 }); 474 }, 475 476 async updateEmail( 477 token: AccessToken, 478 email: string, 479 emailToken?: string, 480 ): Promise<void> { 481 await xrpc("com.atproto.server.updateEmail", { 482 method: "POST", 483 token, 484 body: { email, token: emailToken }, 485 }); 486 }, 487 488 checkEmailUpdateStatus( 489 token: AccessToken, 490 ): Promise<{ pending: boolean; authorized: boolean; newEmail?: string }> { 491 return xrpc("_account.checkEmailUpdateStatus", { 492 method: "GET", 493 token, 494 }); 495 }, 496 497 async updateHandle(token: AccessToken, handle: Handle): Promise<void> { 498 await xrpc("com.atproto.identity.updateHandle", { 499 method: "POST", 500 token, 501 body: { handle }, 502 }); 503 }, 504 505 async requestAccountDelete(token: AccessToken): Promise<void> { 506 await xrpc("com.atproto.server.requestAccountDelete", { 507 method: "POST", 508 token, 509 }); 510 }, 511 512 async deleteAccount( 513 did: Did, 514 password: string, 515 deleteToken: string, 516 ): Promise<void> { 517 await xrpc("com.atproto.server.deleteAccount", { 518 method: "POST", 519 body: { did, password, token: deleteToken }, 520 }); 521 }, 522 523 describeServer(): Promise<ServerDescription> { 524 return xrpc("com.atproto.server.describeServer"); 525 }, 526 527 listRepos(limit?: number): Promise<ListReposResponse> { 528 const params: Record<string, string> = {}; 529 if (limit) params.limit = String(limit); 530 return xrpc("com.atproto.sync.listRepos", { params }); 531 }, 532 533 getNotificationPrefs(token: AccessToken): Promise<NotificationPrefs> { 534 return xrpc("_account.getNotificationPrefs", { token }); 535 }, 536 537 updateNotificationPrefs(token: AccessToken, prefs: { 538 preferredChannel?: string; 539 discordId?: string; 540 telegramUsername?: string; 541 signalNumber?: string; 542 }): Promise<SuccessResponse> { 543 return xrpc("_account.updateNotificationPrefs", { 544 method: "POST", 545 token, 546 body: prefs, 547 }); 548 }, 549 550 confirmChannelVerification( 551 token: AccessToken, 552 channel: string, 553 identifier: string, 554 code: string, 555 ): Promise<SuccessResponse> { 556 return xrpc("_account.confirmChannelVerification", { 557 method: "POST", 558 token, 559 body: { channel, identifier, code }, 560 }); 561 }, 562 563 getNotificationHistory( 564 token: AccessToken, 565 ): Promise<NotificationHistoryResponse> { 566 return xrpc("_account.getNotificationHistory", { token }); 567 }, 568 569 getServerStats(token: AccessToken): Promise<ServerStats> { 570 return xrpc("_admin.getServerStats", { token }); 571 }, 572 573 getServerConfig(): Promise<ServerConfig> { 574 return xrpc("_server.getConfig"); 575 }, 576 577 updateServerConfig( 578 token: AccessToken, 579 config: { 580 serverName?: string; 581 primaryColor?: string; 582 primaryColorDark?: string; 583 secondaryColor?: string; 584 secondaryColorDark?: string; 585 logoCid?: string; 586 }, 587 ): Promise<SuccessResponse> { 588 return xrpc("_admin.updateServerConfig", { 589 method: "POST", 590 token, 591 body: config, 592 }); 593 }, 594 595 async uploadBlob( 596 token: AccessToken, 597 file: File, 598 ): Promise<UploadBlobResponse> { 599 const res = await authenticatedFetch("/xrpc/com.atproto.repo.uploadBlob", { 600 method: "POST", 601 token, 602 headers: { "Content-Type": file.type }, 603 body: file, 604 }); 605 if (!res.ok) { 606 const errData = await res.json().catch(() => ({ 607 error: "Unknown", 608 message: res.statusText, 609 })); 610 throw new ApiError(res.status, errData.error, errData.message); 611 } 612 return res.json(); 613 }, 614 615 async changePassword( 616 token: AccessToken, 617 currentPassword: string, 618 newPassword: string, 619 ): Promise<void> { 620 await xrpc("_account.changePassword", { 621 method: "POST", 622 token, 623 body: { currentPassword, newPassword }, 624 }); 625 }, 626 627 removePassword(token: AccessToken): Promise<SuccessResponse> { 628 return xrpc("_account.removePassword", { 629 method: "POST", 630 token, 631 }); 632 }, 633 634 setPassword( 635 token: AccessToken, 636 newPassword: string, 637 ): Promise<SuccessResponse> { 638 return xrpc("_account.setPassword", { 639 method: "POST", 640 token, 641 body: { newPassword }, 642 }); 643 }, 644 645 getPasswordStatus(token: AccessToken): Promise<PasswordStatus> { 646 return xrpc("_account.getPasswordStatus", { token }); 647 }, 648 649 getLegacyLoginPreference(token: AccessToken): Promise<LegacyLoginPreference> { 650 return xrpc("_account.getLegacyLoginPreference", { token }); 651 }, 652 653 updateLegacyLoginPreference( 654 token: AccessToken, 655 allowLegacyLogin: boolean, 656 ): Promise<UpdateLegacyLoginResponse> { 657 return xrpc("_account.updateLegacyLoginPreference", { 658 method: "POST", 659 token, 660 body: { allowLegacyLogin }, 661 }); 662 }, 663 664 updateLocale( 665 token: AccessToken, 666 preferredLocale: string, 667 ): Promise<UpdateLocaleResponse> { 668 return xrpc("_account.updateLocale", { 669 method: "POST", 670 token, 671 body: { preferredLocale }, 672 }); 673 }, 674 675 listSessions(token: AccessToken): Promise<ListSessionsResponse> { 676 return xrpc("_account.listSessions", { token }); 677 }, 678 679 async revokeSession(token: AccessToken, sessionId: string): Promise<void> { 680 await xrpc("_account.revokeSession", { 681 method: "POST", 682 token, 683 body: { sessionId }, 684 }); 685 }, 686 687 revokeAllSessions(token: AccessToken): Promise<{ revokedCount: number }> { 688 return xrpc("_account.revokeAllSessions", { 689 method: "POST", 690 token, 691 }); 692 }, 693 694 searchAccounts(token: AccessToken, options?: { 695 handle?: string; 696 cursor?: string; 697 limit?: number; 698 }): Promise<SearchAccountsResponse> { 699 const params: Record<string, string> = {}; 700 if (options?.handle) params.handle = options.handle; 701 if (options?.cursor) params.cursor = options.cursor; 702 if (options?.limit) params.limit = String(options.limit); 703 return xrpc("com.atproto.admin.searchAccounts", { token, params }); 704 }, 705 706 getInviteCodes(token: AccessToken, options?: { 707 sort?: "recent" | "usage"; 708 cursor?: string; 709 limit?: number; 710 }): Promise<GetInviteCodesResponse> { 711 const params: Record<string, string> = {}; 712 if (options?.sort) params.sort = options.sort; 713 if (options?.cursor) params.cursor = options.cursor; 714 if (options?.limit) params.limit = String(options.limit); 715 return xrpc("com.atproto.admin.getInviteCodes", { token, params }); 716 }, 717 718 async disableInviteCodes( 719 token: AccessToken, 720 codes?: string[], 721 accounts?: string[], 722 ): Promise<void> { 723 await xrpc("com.atproto.admin.disableInviteCodes", { 724 method: "POST", 725 token, 726 body: { codes, accounts }, 727 }); 728 }, 729 730 getAccountInfo(token: AccessToken, did: Did): Promise<AccountInfo> { 731 return xrpc("com.atproto.admin.getAccountInfo", { token, params: { did } }); 732 }, 733 734 async disableAccountInvites(token: AccessToken, account: Did): Promise<void> { 735 await xrpc("com.atproto.admin.disableAccountInvites", { 736 method: "POST", 737 token, 738 body: { account }, 739 }); 740 }, 741 742 async enableAccountInvites(token: AccessToken, account: Did): Promise<void> { 743 await xrpc("com.atproto.admin.enableAccountInvites", { 744 method: "POST", 745 token, 746 body: { account }, 747 }); 748 }, 749 750 async adminDeleteAccount(token: AccessToken, did: Did): Promise<void> { 751 await xrpc("com.atproto.admin.deleteAccount", { 752 method: "POST", 753 token, 754 body: { did }, 755 }); 756 }, 757 758 describeRepo(token: AccessToken, repo: Did): Promise<RepoDescription> { 759 return xrpc("com.atproto.repo.describeRepo", { 760 token, 761 params: { repo }, 762 }); 763 }, 764 765 listRecords(token: AccessToken, repo: Did, collection: Nsid, options?: { 766 limit?: number; 767 cursor?: string; 768 reverse?: boolean; 769 }): Promise<ListRecordsResponse> { 770 const params: Record<string, string> = { repo, collection }; 771 if (options?.limit) params.limit = String(options.limit); 772 if (options?.cursor) params.cursor = options.cursor; 773 if (options?.reverse) params.reverse = "true"; 774 return xrpc("com.atproto.repo.listRecords", { token, params }); 775 }, 776 777 getRecord( 778 token: AccessToken, 779 repo: Did, 780 collection: Nsid, 781 rkey: Rkey, 782 ): Promise<RecordResponse> { 783 return xrpc("com.atproto.repo.getRecord", { 784 token, 785 params: { repo, collection, rkey }, 786 }); 787 }, 788 789 createRecord( 790 token: AccessToken, 791 repo: Did, 792 collection: Nsid, 793 record: unknown, 794 rkey?: Rkey, 795 ): Promise<CreateRecordResponse> { 796 return xrpc("com.atproto.repo.createRecord", { 797 method: "POST", 798 token, 799 body: { repo, collection, record, rkey }, 800 }); 801 }, 802 803 putRecord( 804 token: AccessToken, 805 repo: Did, 806 collection: Nsid, 807 rkey: Rkey, 808 record: unknown, 809 ): Promise<CreateRecordResponse> { 810 return xrpc("com.atproto.repo.putRecord", { 811 method: "POST", 812 token, 813 body: { repo, collection, rkey, record }, 814 }); 815 }, 816 817 async deleteRecord( 818 token: AccessToken, 819 repo: Did, 820 collection: Nsid, 821 rkey: Rkey, 822 ): Promise<void> { 823 await xrpc("com.atproto.repo.deleteRecord", { 824 method: "POST", 825 token, 826 body: { repo, collection, rkey }, 827 }); 828 }, 829 830 getTotpStatus(token: AccessToken): Promise<TotpStatus> { 831 return xrpc("com.atproto.server.getTotpStatus", { token }); 832 }, 833 834 createTotpSecret(token: AccessToken): Promise<TotpSecret> { 835 return xrpc("com.atproto.server.createTotpSecret", { 836 method: "POST", 837 token, 838 }); 839 }, 840 841 enableTotp(token: AccessToken, code: string): Promise<EnableTotpResponse> { 842 return xrpc("com.atproto.server.enableTotp", { 843 method: "POST", 844 token, 845 body: { code }, 846 }); 847 }, 848 849 disableTotp( 850 token: AccessToken, 851 password: string, 852 code: string, 853 ): Promise<SuccessResponse> { 854 return xrpc("com.atproto.server.disableTotp", { 855 method: "POST", 856 token, 857 body: { password, code }, 858 }); 859 }, 860 861 regenerateBackupCodes( 862 token: AccessToken, 863 password: string, 864 code: string, 865 ): Promise<RegenerateBackupCodesResponse> { 866 return xrpc("com.atproto.server.regenerateBackupCodes", { 867 method: "POST", 868 token, 869 body: { password, code }, 870 }); 871 }, 872 873 startPasskeyRegistration( 874 token: AccessToken, 875 friendlyName?: string, 876 ): Promise<StartPasskeyRegistrationResponse> { 877 return xrpc("com.atproto.server.startPasskeyRegistration", { 878 method: "POST", 879 token, 880 body: { friendlyName }, 881 }); 882 }, 883 884 finishPasskeyRegistration( 885 token: AccessToken, 886 credential: unknown, 887 friendlyName?: string, 888 ): Promise<FinishPasskeyRegistrationResponse> { 889 return xrpc("com.atproto.server.finishPasskeyRegistration", { 890 method: "POST", 891 token, 892 body: { credential, friendlyName }, 893 }); 894 }, 895 896 listPasskeys(token: AccessToken): Promise<ListPasskeysResponse> { 897 return xrpc("com.atproto.server.listPasskeys", { token }); 898 }, 899 900 async deletePasskey(token: AccessToken, id: string): Promise<void> { 901 await xrpc("com.atproto.server.deletePasskey", { 902 method: "POST", 903 token, 904 body: { id }, 905 }); 906 }, 907 908 async updatePasskey( 909 token: AccessToken, 910 id: string, 911 friendlyName: string, 912 ): Promise<void> { 913 await xrpc("com.atproto.server.updatePasskey", { 914 method: "POST", 915 token, 916 body: { id, friendlyName }, 917 }); 918 }, 919 920 listTrustedDevices(token: AccessToken): Promise<ListTrustedDevicesResponse> { 921 return xrpc("_account.listTrustedDevices", { token }); 922 }, 923 924 revokeTrustedDevice( 925 token: AccessToken, 926 deviceId: string, 927 ): Promise<SuccessResponse> { 928 return xrpc("_account.revokeTrustedDevice", { 929 method: "POST", 930 token, 931 body: { deviceId }, 932 }); 933 }, 934 935 updateTrustedDevice( 936 token: AccessToken, 937 deviceId: string, 938 friendlyName: string, 939 ): Promise<SuccessResponse> { 940 return xrpc("_account.updateTrustedDevice", { 941 method: "POST", 942 token, 943 body: { deviceId, friendlyName }, 944 }); 945 }, 946 947 getReauthStatus(token: AccessToken): Promise<ReauthStatus> { 948 return xrpc("_account.getReauthStatus", { token }); 949 }, 950 951 reauthPassword( 952 token: AccessToken, 953 password: string, 954 ): Promise<ReauthResponse> { 955 return xrpc("_account.reauthPassword", { 956 method: "POST", 957 token, 958 body: { password }, 959 }); 960 }, 961 962 reauthTotp(token: AccessToken, code: string): Promise<ReauthResponse> { 963 return xrpc("_account.reauthTotp", { 964 method: "POST", 965 token, 966 body: { code }, 967 }); 968 }, 969 970 reauthPasskeyStart(token: AccessToken): Promise<ReauthPasskeyStartResponse> { 971 return xrpc("_account.reauthPasskeyStart", { 972 method: "POST", 973 token, 974 }); 975 }, 976 977 reauthPasskeyFinish( 978 token: AccessToken, 979 credential: unknown, 980 ): Promise<ReauthResponse> { 981 return xrpc("_account.reauthPasskeyFinish", { 982 method: "POST", 983 token, 984 body: { credential }, 985 }); 986 }, 987 988 reserveSigningKey(did?: Did): Promise<ReserveSigningKeyResponse> { 989 return xrpc("com.atproto.server.reserveSigningKey", { 990 method: "POST", 991 body: { did }, 992 }); 993 }, 994 995 getRecommendedDidCredentials( 996 token: AccessToken, 997 ): Promise<RecommendedDidCredentials> { 998 return xrpc("com.atproto.identity.getRecommendedDidCredentials", { token }); 999 }, 1000 1001 async activateAccount(token: AccessToken): Promise<void> { 1002 await xrpc("com.atproto.server.activateAccount", { 1003 method: "POST", 1004 token, 1005 }); 1006 }, 1007 1008 async createPasskeyAccount(params: { 1009 handle: Handle; 1010 email?: EmailAddress; 1011 inviteCode?: string; 1012 didType?: DidType; 1013 did?: Did; 1014 signingKey?: string; 1015 verificationChannel?: VerificationChannel; 1016 discordId?: string; 1017 telegramUsername?: string; 1018 signalNumber?: string; 1019 }, byodToken?: string): Promise<PasskeyAccountCreateResponse> { 1020 const url = `${API_BASE}/_account.createPasskeyAccount`; 1021 const headers: Record<string, string> = { 1022 "Content-Type": "application/json", 1023 }; 1024 if (byodToken) { 1025 headers["Authorization"] = `Bearer ${byodToken}`; 1026 } 1027 const res = await fetch(url, { 1028 method: "POST", 1029 headers, 1030 body: JSON.stringify(params), 1031 }); 1032 if (!res.ok) { 1033 const errData = await res.json().catch(() => ({ 1034 error: "Unknown", 1035 message: res.statusText, 1036 })); 1037 throw new ApiError(res.status, errData.error, errData.message); 1038 } 1039 return res.json(); 1040 }, 1041 1042 startPasskeyRegistrationForSetup( 1043 did: Did, 1044 setupToken: string, 1045 friendlyName?: string, 1046 ): Promise<StartPasskeyRegistrationResponse> { 1047 return xrpc("_account.startPasskeyRegistrationForSetup", { 1048 method: "POST", 1049 body: { did, setupToken, friendlyName }, 1050 }); 1051 }, 1052 1053 completePasskeySetup( 1054 did: Did, 1055 setupToken: string, 1056 passkeyCredential: unknown, 1057 passkeyFriendlyName?: string, 1058 ): Promise<CompletePasskeySetupResponse> { 1059 return xrpc("_account.completePasskeySetup", { 1060 method: "POST", 1061 body: { did, setupToken, passkeyCredential, passkeyFriendlyName }, 1062 }); 1063 }, 1064 1065 requestPasskeyRecovery(email: EmailAddress): Promise<SuccessResponse> { 1066 return xrpc("_account.requestPasskeyRecovery", { 1067 method: "POST", 1068 body: { email }, 1069 }); 1070 }, 1071 1072 recoverPasskeyAccount( 1073 did: Did, 1074 recoveryToken: string, 1075 newPassword: string, 1076 ): Promise<SuccessResponse> { 1077 return xrpc("_account.recoverPasskeyAccount", { 1078 method: "POST", 1079 body: { did, recoveryToken, newPassword }, 1080 }); 1081 }, 1082 1083 verifyMigrationEmail( 1084 token: string, 1085 email: EmailAddress, 1086 ): Promise<VerifyMigrationEmailResponse> { 1087 return xrpc("com.atproto.server.verifyMigrationEmail", { 1088 method: "POST", 1089 body: { token, email }, 1090 }); 1091 }, 1092 1093 resendMigrationVerification( 1094 email: EmailAddress, 1095 ): Promise<ResendMigrationVerificationResponse> { 1096 return xrpc("com.atproto.server.resendMigrationVerification", { 1097 method: "POST", 1098 body: { email }, 1099 }); 1100 }, 1101 1102 verifyToken( 1103 token: string, 1104 identifier: string, 1105 accessToken?: AccessToken, 1106 ): Promise<VerifyTokenResponse> { 1107 return xrpc("_account.verifyToken", { 1108 method: "POST", 1109 body: { token, identifier }, 1110 token: accessToken, 1111 }); 1112 }, 1113 1114 getDidDocument(token: AccessToken): Promise<DidDocument> { 1115 return xrpc("_account.getDidDocument", { token }); 1116 }, 1117 1118 updateDidDocument( 1119 token: AccessToken, 1120 params: { 1121 verificationMethods?: VerificationMethod[]; 1122 alsoKnownAs?: string[]; 1123 serviceEndpoint?: string; 1124 }, 1125 ): Promise<SuccessResponse> { 1126 return xrpc("_account.updateDidDocument", { 1127 method: "POST", 1128 token, 1129 body: params, 1130 }); 1131 }, 1132 1133 async deactivateAccount( 1134 token: AccessToken, 1135 deleteAfter?: string, 1136 ): Promise<void> { 1137 await xrpc("com.atproto.server.deactivateAccount", { 1138 method: "POST", 1139 token, 1140 body: { deleteAfter }, 1141 }); 1142 }, 1143 1144 async getRepo(token: AccessToken, did: Did): Promise<ArrayBuffer> { 1145 const url = `${API_BASE}/com.atproto.sync.getRepo?did=${encodeURIComponent(did)}`; 1146 const res = await authenticatedFetch(url, { token }); 1147 if (!res.ok) { 1148 const errData = await res.json().catch(() => ({ 1149 error: "Unknown", 1150 message: res.statusText, 1151 })); 1152 throw new ApiError(res.status, errData.error, errData.message); 1153 } 1154 return res.arrayBuffer(); 1155 }, 1156 1157 listBackups(token: AccessToken): Promise<ListBackupsResponse> { 1158 return xrpc("_backup.listBackups", { token }); 1159 }, 1160 1161 async getBackup(token: AccessToken, id: string): Promise<Blob> { 1162 const url = `${API_BASE}/_backup.getBackup?id=${encodeURIComponent(id)}`; 1163 const res = await authenticatedFetch(url, { token }); 1164 if (!res.ok) { 1165 const errData = await res.json().catch(() => ({ 1166 error: "Unknown", 1167 message: res.statusText, 1168 })); 1169 throw new ApiError(res.status, errData.error, errData.message); 1170 } 1171 return res.blob(); 1172 }, 1173 1174 createBackup(token: AccessToken): Promise<CreateBackupResponse> { 1175 return xrpc("_backup.createBackup", { 1176 method: "POST", 1177 token, 1178 }); 1179 }, 1180 1181 async deleteBackup(token: AccessToken, id: string): Promise<void> { 1182 await xrpc("_backup.deleteBackup", { 1183 method: "POST", 1184 token, 1185 params: { id }, 1186 }); 1187 }, 1188 1189 setBackupEnabled( 1190 token: AccessToken, 1191 enabled: boolean, 1192 ): Promise<SetBackupEnabledResponse> { 1193 return xrpc("_backup.setEnabled", { 1194 method: "POST", 1195 token, 1196 body: { enabled }, 1197 }); 1198 }, 1199 1200 async importRepo(token: AccessToken, car: Uint8Array): Promise<void> { 1201 const res = await authenticatedFetch(`${API_BASE}/com.atproto.repo.importRepo`, { 1202 method: "POST", 1203 token, 1204 headers: { "Content-Type": "application/vnd.ipld.car" }, 1205 body: car as unknown as BodyInit, 1206 }); 1207 if (!res.ok) { 1208 const errData = await res.json().catch(() => ({ 1209 error: "Unknown", 1210 message: res.statusText, 1211 })); 1212 throw new ApiError(res.status, errData.error, errData.message); 1213 } 1214 }, 1215 1216 async establishOAuthSession(token: AccessToken): Promise<{ success: boolean; device_id: string }> { 1217 const res = await authenticatedFetch("/oauth/establish-session", { 1218 method: "POST", 1219 token, 1220 headers: { "Content-Type": "application/json" }, 1221 }); 1222 if (!res.ok) { 1223 const errData = await res.json().catch(() => ({ 1224 error: "Unknown", 1225 message: res.statusText, 1226 })); 1227 throw new ApiError(res.status, errData.error, errData.message); 1228 } 1229 return res.json(); 1230 }, 1231}; 1232 1233export const typedApi = { 1234 createSession( 1235 identifier: string, 1236 password: string, 1237 ): Promise<Result<Session, ApiError>> { 1238 return xrpcResult<Session>("com.atproto.server.createSession", { 1239 method: "POST", 1240 body: { identifier, password }, 1241 }).then((r) => r.ok ? ok(castSession(r.value)) : r); 1242 }, 1243 1244 getSession(token: AccessToken): Promise<Result<Session, ApiError>> { 1245 return xrpcResult<Session>("com.atproto.server.getSession", { token }) 1246 .then((r) => r.ok ? ok(castSession(r.value)) : r); 1247 }, 1248 1249 refreshSession(refreshJwt: RefreshToken): Promise<Result<Session, ApiError>> { 1250 return xrpcResult<Session>("com.atproto.server.refreshSession", { 1251 method: "POST", 1252 token: refreshJwt, 1253 }).then((r) => r.ok ? ok(castSession(r.value)) : r); 1254 }, 1255 1256 describeServer(): Promise<Result<ServerDescription, ApiError>> { 1257 return xrpcResult("com.atproto.server.describeServer"); 1258 }, 1259 1260 listAppPasswords( 1261 token: AccessToken, 1262 ): Promise<Result<{ passwords: AppPassword[] }, ApiError>> { 1263 return xrpcResult("com.atproto.server.listAppPasswords", { token }); 1264 }, 1265 1266 createAppPassword( 1267 token: AccessToken, 1268 name: string, 1269 scopes?: string, 1270 ): Promise<Result<CreatedAppPassword, ApiError>> { 1271 return xrpcResult("com.atproto.server.createAppPassword", { 1272 method: "POST", 1273 token, 1274 body: { name, scopes }, 1275 }); 1276 }, 1277 1278 revokeAppPassword( 1279 token: AccessToken, 1280 name: string, 1281 ): Promise<Result<void, ApiError>> { 1282 return xrpcResult<void>("com.atproto.server.revokeAppPassword", { 1283 method: "POST", 1284 token, 1285 body: { name }, 1286 }); 1287 }, 1288 1289 listSessions( 1290 token: AccessToken, 1291 ): Promise<Result<ListSessionsResponse, ApiError>> { 1292 return xrpcResult("_account.listSessions", { token }); 1293 }, 1294 1295 revokeSession( 1296 token: AccessToken, 1297 sessionId: string, 1298 ): Promise<Result<void, ApiError>> { 1299 return xrpcResult<void>("_account.revokeSession", { 1300 method: "POST", 1301 token, 1302 body: { sessionId }, 1303 }); 1304 }, 1305 1306 getTotpStatus(token: AccessToken): Promise<Result<TotpStatus, ApiError>> { 1307 return xrpcResult("com.atproto.server.getTotpStatus", { token }); 1308 }, 1309 1310 createTotpSecret(token: AccessToken): Promise<Result<TotpSecret, ApiError>> { 1311 return xrpcResult("com.atproto.server.createTotpSecret", { 1312 method: "POST", 1313 token, 1314 }); 1315 }, 1316 1317 enableTotp( 1318 token: AccessToken, 1319 code: string, 1320 ): Promise<Result<EnableTotpResponse, ApiError>> { 1321 return xrpcResult("com.atproto.server.enableTotp", { 1322 method: "POST", 1323 token, 1324 body: { code }, 1325 }); 1326 }, 1327 1328 disableTotp( 1329 token: AccessToken, 1330 password: string, 1331 code: string, 1332 ): Promise<Result<SuccessResponse, ApiError>> { 1333 return xrpcResult("com.atproto.server.disableTotp", { 1334 method: "POST", 1335 token, 1336 body: { password, code }, 1337 }); 1338 }, 1339 1340 listPasskeys( 1341 token: AccessToken, 1342 ): Promise<Result<ListPasskeysResponse, ApiError>> { 1343 return xrpcResult("com.atproto.server.listPasskeys", { token }); 1344 }, 1345 1346 deletePasskey( 1347 token: AccessToken, 1348 id: string, 1349 ): Promise<Result<void, ApiError>> { 1350 return xrpcResult<void>("com.atproto.server.deletePasskey", { 1351 method: "POST", 1352 token, 1353 body: { id }, 1354 }); 1355 }, 1356 1357 listTrustedDevices( 1358 token: AccessToken, 1359 ): Promise<Result<ListTrustedDevicesResponse, ApiError>> { 1360 return xrpcResult("_account.listTrustedDevices", { token }); 1361 }, 1362 1363 getReauthStatus(token: AccessToken): Promise<Result<ReauthStatus, ApiError>> { 1364 return xrpcResult("_account.getReauthStatus", { token }); 1365 }, 1366 1367 getNotificationPrefs( 1368 token: AccessToken, 1369 ): Promise<Result<NotificationPrefs, ApiError>> { 1370 return xrpcResult("_account.getNotificationPrefs", { token }); 1371 }, 1372 1373 updateHandle( 1374 token: AccessToken, 1375 handle: Handle, 1376 ): Promise<Result<void, ApiError>> { 1377 return xrpcResult<void>("com.atproto.identity.updateHandle", { 1378 method: "POST", 1379 token, 1380 body: { handle }, 1381 }); 1382 }, 1383 1384 describeRepo( 1385 token: AccessToken, 1386 repo: Did, 1387 ): Promise<Result<RepoDescription, ApiError>> { 1388 return xrpcResult("com.atproto.repo.describeRepo", { 1389 token, 1390 params: { repo }, 1391 }); 1392 }, 1393 1394 listRecords( 1395 token: AccessToken, 1396 repo: Did, 1397 collection: Nsid, 1398 options?: { limit?: number; cursor?: string; reverse?: boolean }, 1399 ): Promise<Result<ListRecordsResponse, ApiError>> { 1400 const params: Record<string, string> = { repo, collection }; 1401 if (options?.limit) params.limit = String(options.limit); 1402 if (options?.cursor) params.cursor = options.cursor; 1403 if (options?.reverse) params.reverse = "true"; 1404 return xrpcResult("com.atproto.repo.listRecords", { token, params }); 1405 }, 1406 1407 getRecord( 1408 token: AccessToken, 1409 repo: Did, 1410 collection: Nsid, 1411 rkey: Rkey, 1412 ): Promise<Result<RecordResponse, ApiError>> { 1413 return xrpcResult("com.atproto.repo.getRecord", { 1414 token, 1415 params: { repo, collection, rkey }, 1416 }); 1417 }, 1418 1419 deleteRecord( 1420 token: AccessToken, 1421 repo: Did, 1422 collection: Nsid, 1423 rkey: Rkey, 1424 ): Promise<Result<void, ApiError>> { 1425 return xrpcResult<void>("com.atproto.repo.deleteRecord", { 1426 method: "POST", 1427 token, 1428 body: { repo, collection, rkey }, 1429 }); 1430 }, 1431 1432 searchAccounts( 1433 token: AccessToken, 1434 options?: { handle?: string; cursor?: string; limit?: number }, 1435 ): Promise<Result<SearchAccountsResponse, ApiError>> { 1436 const params: Record<string, string> = {}; 1437 if (options?.handle) params.handle = options.handle; 1438 if (options?.cursor) params.cursor = options.cursor; 1439 if (options?.limit) params.limit = String(options.limit); 1440 return xrpcResult("com.atproto.admin.searchAccounts", { token, params }); 1441 }, 1442 1443 getAccountInfo( 1444 token: AccessToken, 1445 did: Did, 1446 ): Promise<Result<AccountInfo, ApiError>> { 1447 return xrpcResult("com.atproto.admin.getAccountInfo", { 1448 token, 1449 params: { did }, 1450 }); 1451 }, 1452 1453 getServerStats(token: AccessToken): Promise<Result<ServerStats, ApiError>> { 1454 return xrpcResult("_admin.getServerStats", { token }); 1455 }, 1456 1457 listBackups( 1458 token: AccessToken, 1459 ): Promise<Result<ListBackupsResponse, ApiError>> { 1460 return xrpcResult("_backup.listBackups", { token }); 1461 }, 1462 1463 createBackup( 1464 token: AccessToken, 1465 ): Promise<Result<CreateBackupResponse, ApiError>> { 1466 return xrpcResult("_backup.createBackup", { 1467 method: "POST", 1468 token, 1469 }); 1470 }, 1471 1472 getDidDocument(token: AccessToken): Promise<Result<DidDocument, ApiError>> { 1473 return xrpcResult("_account.getDidDocument", { token }); 1474 }, 1475 1476 deleteSession(token: AccessToken): Promise<Result<void, ApiError>> { 1477 return xrpcResult<void>("com.atproto.server.deleteSession", { 1478 method: "POST", 1479 token, 1480 }); 1481 }, 1482 1483 revokeAllSessions( 1484 token: AccessToken, 1485 ): Promise<Result<{ revokedCount: number }, ApiError>> { 1486 return xrpcResult("_account.revokeAllSessions", { 1487 method: "POST", 1488 token, 1489 }); 1490 }, 1491 1492 getAccountInviteCodes( 1493 token: AccessToken, 1494 ): Promise<Result<{ codes: InviteCodeInfo[] }, ApiError>> { 1495 return xrpcResult("com.atproto.server.getAccountInviteCodes", { token }); 1496 }, 1497 1498 createInviteCode( 1499 token: AccessToken, 1500 useCount: number = 1, 1501 ): Promise<Result<{ code: string }, ApiError>> { 1502 return xrpcResult("com.atproto.server.createInviteCode", { 1503 method: "POST", 1504 token, 1505 body: { useCount }, 1506 }); 1507 }, 1508 1509 changePassword( 1510 token: AccessToken, 1511 currentPassword: string, 1512 newPassword: string, 1513 ): Promise<Result<void, ApiError>> { 1514 return xrpcResult<void>("_account.changePassword", { 1515 method: "POST", 1516 token, 1517 body: { currentPassword, newPassword }, 1518 }); 1519 }, 1520 1521 getPasswordStatus( 1522 token: AccessToken, 1523 ): Promise<Result<PasswordStatus, ApiError>> { 1524 return xrpcResult("_account.getPasswordStatus", { token }); 1525 }, 1526 1527 getServerConfig(): Promise<Result<ServerConfig, ApiError>> { 1528 return xrpcResult("_server.getConfig"); 1529 }, 1530 1531 getLegacyLoginPreference( 1532 token: AccessToken, 1533 ): Promise<Result<LegacyLoginPreference, ApiError>> { 1534 return xrpcResult("_account.getLegacyLoginPreference", { token }); 1535 }, 1536 1537 updateLegacyLoginPreference( 1538 token: AccessToken, 1539 allowLegacyLogin: boolean, 1540 ): Promise<Result<UpdateLegacyLoginResponse, ApiError>> { 1541 return xrpcResult("_account.updateLegacyLoginPreference", { 1542 method: "POST", 1543 token, 1544 body: { allowLegacyLogin }, 1545 }); 1546 }, 1547 1548 getNotificationHistory( 1549 token: AccessToken, 1550 ): Promise<Result<NotificationHistoryResponse, ApiError>> { 1551 return xrpcResult("_account.getNotificationHistory", { token }); 1552 }, 1553 1554 updateNotificationPrefs( 1555 token: AccessToken, 1556 prefs: { 1557 preferredChannel?: string; 1558 discordId?: string; 1559 telegramUsername?: string; 1560 signalNumber?: string; 1561 }, 1562 ): Promise<Result<SuccessResponse, ApiError>> { 1563 return xrpcResult("_account.updateNotificationPrefs", { 1564 method: "POST", 1565 token, 1566 body: prefs, 1567 }); 1568 }, 1569 1570 revokeTrustedDevice( 1571 token: AccessToken, 1572 deviceId: string, 1573 ): Promise<Result<SuccessResponse, ApiError>> { 1574 return xrpcResult("_account.revokeTrustedDevice", { 1575 method: "POST", 1576 token, 1577 body: { deviceId }, 1578 }); 1579 }, 1580 1581 updateTrustedDevice( 1582 token: AccessToken, 1583 deviceId: string, 1584 friendlyName: string, 1585 ): Promise<Result<SuccessResponse, ApiError>> { 1586 return xrpcResult("_account.updateTrustedDevice", { 1587 method: "POST", 1588 token, 1589 body: { deviceId, friendlyName }, 1590 }); 1591 }, 1592 1593 reauthPassword( 1594 token: AccessToken, 1595 password: string, 1596 ): Promise<Result<ReauthResponse, ApiError>> { 1597 return xrpcResult("_account.reauthPassword", { 1598 method: "POST", 1599 token, 1600 body: { password }, 1601 }); 1602 }, 1603 1604 reauthTotp( 1605 token: AccessToken, 1606 code: string, 1607 ): Promise<Result<ReauthResponse, ApiError>> { 1608 return xrpcResult("_account.reauthTotp", { 1609 method: "POST", 1610 token, 1611 body: { code }, 1612 }); 1613 }, 1614 1615 reauthPasskeyStart( 1616 token: AccessToken, 1617 ): Promise<Result<ReauthPasskeyStartResponse, ApiError>> { 1618 return xrpcResult("_account.reauthPasskeyStart", { 1619 method: "POST", 1620 token, 1621 }); 1622 }, 1623 1624 reauthPasskeyFinish( 1625 token: AccessToken, 1626 credential: unknown, 1627 ): Promise<Result<ReauthResponse, ApiError>> { 1628 return xrpcResult("_account.reauthPasskeyFinish", { 1629 method: "POST", 1630 token, 1631 body: { credential }, 1632 }); 1633 }, 1634 1635 confirmSignup( 1636 did: Did, 1637 verificationCode: string, 1638 ): Promise<Result<ConfirmSignupResult, ApiError>> { 1639 return xrpcResult("com.atproto.server.confirmSignup", { 1640 method: "POST", 1641 body: { did, verificationCode }, 1642 }); 1643 }, 1644 1645 resendVerification( 1646 did: Did, 1647 ): Promise<Result<{ success: boolean }, ApiError>> { 1648 return xrpcResult("com.atproto.server.resendVerification", { 1649 method: "POST", 1650 body: { did }, 1651 }); 1652 }, 1653 1654 requestEmailUpdate( 1655 token: AccessToken, 1656 ): Promise<Result<EmailUpdateResponse, ApiError>> { 1657 return xrpcResult("com.atproto.server.requestEmailUpdate", { 1658 method: "POST", 1659 token, 1660 }); 1661 }, 1662 1663 updateEmail( 1664 token: AccessToken, 1665 email: string, 1666 emailToken?: string, 1667 ): Promise<Result<void, ApiError>> { 1668 return xrpcResult<void>("com.atproto.server.updateEmail", { 1669 method: "POST", 1670 token, 1671 body: { email, token: emailToken }, 1672 }); 1673 }, 1674 1675 requestAccountDelete(token: AccessToken): Promise<Result<void, ApiError>> { 1676 return xrpcResult<void>("com.atproto.server.requestAccountDelete", { 1677 method: "POST", 1678 token, 1679 }); 1680 }, 1681 1682 deleteAccount( 1683 did: Did, 1684 password: string, 1685 deleteToken: string, 1686 ): Promise<Result<void, ApiError>> { 1687 return xrpcResult<void>("com.atproto.server.deleteAccount", { 1688 method: "POST", 1689 body: { did, password, token: deleteToken }, 1690 }); 1691 }, 1692 1693 updateDidDocument( 1694 token: AccessToken, 1695 params: { 1696 verificationMethods?: VerificationMethod[]; 1697 alsoKnownAs?: string[]; 1698 serviceEndpoint?: string; 1699 }, 1700 ): Promise<Result<SuccessResponse, ApiError>> { 1701 return xrpcResult("_account.updateDidDocument", { 1702 method: "POST", 1703 token, 1704 body: params, 1705 }); 1706 }, 1707 1708 deactivateAccount( 1709 token: AccessToken, 1710 deleteAfter?: string, 1711 ): Promise<Result<void, ApiError>> { 1712 return xrpcResult<void>("com.atproto.server.deactivateAccount", { 1713 method: "POST", 1714 token, 1715 body: { deleteAfter }, 1716 }); 1717 }, 1718 1719 activateAccount(token: AccessToken): Promise<Result<void, ApiError>> { 1720 return xrpcResult<void>("com.atproto.server.activateAccount", { 1721 method: "POST", 1722 token, 1723 }); 1724 }, 1725 1726 setBackupEnabled( 1727 token: AccessToken, 1728 enabled: boolean, 1729 ): Promise<Result<SetBackupEnabledResponse, ApiError>> { 1730 return xrpcResult("_backup.setEnabled", { 1731 method: "POST", 1732 token, 1733 body: { enabled }, 1734 }); 1735 }, 1736 1737 deleteBackup( 1738 token: AccessToken, 1739 id: string, 1740 ): Promise<Result<void, ApiError>> { 1741 return xrpcResult<void>("_backup.deleteBackup", { 1742 method: "POST", 1743 token, 1744 params: { id }, 1745 }); 1746 }, 1747 1748 createRecord( 1749 token: AccessToken, 1750 repo: Did, 1751 collection: Nsid, 1752 record: unknown, 1753 rkey?: Rkey, 1754 ): Promise<Result<CreateRecordResponse, ApiError>> { 1755 return xrpcResult("com.atproto.repo.createRecord", { 1756 method: "POST", 1757 token, 1758 body: { repo, collection, record, rkey }, 1759 }); 1760 }, 1761 1762 putRecord( 1763 token: AccessToken, 1764 repo: Did, 1765 collection: Nsid, 1766 rkey: Rkey, 1767 record: unknown, 1768 ): Promise<Result<CreateRecordResponse, ApiError>> { 1769 return xrpcResult("com.atproto.repo.putRecord", { 1770 method: "POST", 1771 token, 1772 body: { repo, collection, rkey, record }, 1773 }); 1774 }, 1775 1776 getInviteCodes( 1777 token: AccessToken, 1778 options?: { sort?: "recent" | "usage"; cursor?: string; limit?: number }, 1779 ): Promise<Result<GetInviteCodesResponse, ApiError>> { 1780 const params: Record<string, string> = {}; 1781 if (options?.sort) params.sort = options.sort; 1782 if (options?.cursor) params.cursor = options.cursor; 1783 if (options?.limit) params.limit = String(options.limit); 1784 return xrpcResult("com.atproto.admin.getInviteCodes", { token, params }); 1785 }, 1786 1787 disableAccountInvites( 1788 token: AccessToken, 1789 account: Did, 1790 ): Promise<Result<void, ApiError>> { 1791 return xrpcResult<void>("com.atproto.admin.disableAccountInvites", { 1792 method: "POST", 1793 token, 1794 body: { account }, 1795 }); 1796 }, 1797 1798 enableAccountInvites( 1799 token: AccessToken, 1800 account: Did, 1801 ): Promise<Result<void, ApiError>> { 1802 return xrpcResult<void>("com.atproto.admin.enableAccountInvites", { 1803 method: "POST", 1804 token, 1805 body: { account }, 1806 }); 1807 }, 1808 1809 adminDeleteAccount( 1810 token: AccessToken, 1811 did: Did, 1812 ): Promise<Result<void, ApiError>> { 1813 return xrpcResult<void>("com.atproto.admin.deleteAccount", { 1814 method: "POST", 1815 token, 1816 body: { did }, 1817 }); 1818 }, 1819 1820 startPasskeyRegistration( 1821 token: AccessToken, 1822 friendlyName?: string, 1823 ): Promise<Result<StartPasskeyRegistrationResponse, ApiError>> { 1824 return xrpcResult("com.atproto.server.startPasskeyRegistration", { 1825 method: "POST", 1826 token, 1827 body: { friendlyName }, 1828 }); 1829 }, 1830 1831 finishPasskeyRegistration( 1832 token: AccessToken, 1833 credential: unknown, 1834 friendlyName?: string, 1835 ): Promise<Result<FinishPasskeyRegistrationResponse, ApiError>> { 1836 return xrpcResult("com.atproto.server.finishPasskeyRegistration", { 1837 method: "POST", 1838 token, 1839 body: { credential, friendlyName }, 1840 }); 1841 }, 1842 1843 updatePasskey( 1844 token: AccessToken, 1845 id: string, 1846 friendlyName: string, 1847 ): Promise<Result<void, ApiError>> { 1848 return xrpcResult<void>("com.atproto.server.updatePasskey", { 1849 method: "POST", 1850 token, 1851 body: { id, friendlyName }, 1852 }); 1853 }, 1854 1855 regenerateBackupCodes( 1856 token: AccessToken, 1857 password: string, 1858 code: string, 1859 ): Promise<Result<RegenerateBackupCodesResponse, ApiError>> { 1860 return xrpcResult("com.atproto.server.regenerateBackupCodes", { 1861 method: "POST", 1862 token, 1863 body: { password, code }, 1864 }); 1865 }, 1866 1867 updateLocale( 1868 token: AccessToken, 1869 preferredLocale: string, 1870 ): Promise<Result<UpdateLocaleResponse, ApiError>> { 1871 return xrpcResult("_account.updateLocale", { 1872 method: "POST", 1873 token, 1874 body: { preferredLocale }, 1875 }); 1876 }, 1877 1878 confirmChannelVerification( 1879 token: AccessToken, 1880 channel: string, 1881 identifier: string, 1882 code: string, 1883 ): Promise<Result<SuccessResponse, ApiError>> { 1884 return xrpcResult("_account.confirmChannelVerification", { 1885 method: "POST", 1886 token, 1887 body: { channel, identifier, code }, 1888 }); 1889 }, 1890 1891 removePassword( 1892 token: AccessToken, 1893 ): Promise<Result<SuccessResponse, ApiError>> { 1894 return xrpcResult("_account.removePassword", { 1895 method: "POST", 1896 token, 1897 }); 1898 }, 1899};