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