Our Personal Data Server from scratch!
0

Configure Feed

Select the types of activity you want to include in your feed.

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