// Generated TypeScript client for AT Protocol records // Generated at: 2025-08-28 00:22:31 UTC // Lexicons: 2 /** * @example Usage * ```ts * import { AtProtoClient } from "./generated_client.ts"; * * const client = new AtProtoClient( * 'https://slices-api.fly.dev', * 'at://did:plc:bcgltzqazw5tb6k2g3ttenbj/social.slices.slice/3lx5y476bws2q' * ); * * // List records from the app.bsky.actor.profile collection * const records = await client.app.bsky.actor.profile.listRecords(); * * // Get a specific record * const record = await client.app.bsky.actor.profile.getRecord({ * uri: 'at://did:plc:example/app.bsky.actor.profile/3abc123' * }); * * // Search records in the collection * const searchResults = await client.app.bsky.actor.profile.searchRecords({ * query: "example search term" * }); * * // Search specific field * const fieldSearch = await client.app.bsky.actor.profile.searchRecords({ * query: "blog", * field: "title" * }); * * // Serve the records as JSON * Deno.serve(async () => new Response(JSON.stringify(records.records.map(r => r.value)))); * ``` */ import { OAuthClient } from "@slices/oauth"; export interface RecordResponse { uri: string; cid: string; did: string; collection: string; value: T; indexedAt: string; } export interface ListRecordsResponse { records: RecordResponse[]; cursor?: string; } export interface GetActorsResponse { actors: Actor[]; cursor?: string; } export interface ListRecordsParams { author?: string; authors?: string[]; limit?: number; cursor?: string; sort?: | `${TSortField}:${"asc" | "desc"}` | `${TSortField}:${"asc" | "desc"},${TSortField}:${"asc" | "desc"}`; } export interface GetRecordParams { uri: string; } export interface GetActorsParams { search?: string; dids?: string[]; limit?: number; cursor?: string; } export interface SearchRecordsParams { query: string; field?: string; limit?: number; cursor?: string; sort?: | `${TSortField}:${"asc" | "desc"}` | `${TSortField}:${"asc" | "desc"},${TSortField}:${"asc" | "desc"}`; } export interface IndexedRecord { uri: string; cid: string; did: string; collection: string; value: Record; indexedAt: string; } export interface Actor { did: string; handle?: string; sliceUri: string; indexedAt: string; } export interface CodegenXrpcRequest { target: string; slice: string; } export interface CodegenXrpcResponse { success: boolean; generatedCode?: string; error?: string; } export interface BulkSyncParams { collections?: string[]; externalCollections?: string[]; repos?: string[]; limitPerRepo?: number; } export interface BulkSyncOutput { success: boolean; totalRecords: number; collectionsSynced: string[]; reposProcessed: number; message: string; } export interface SyncJobResponse { success: boolean; jobId?: string; message: string; } export interface SyncJobResult { success: boolean; totalRecords: number; collectionsSynced: string[]; reposProcessed: number; message: string; } export interface JobStatus { jobId: string; status: string; createdAt: string; startedAt?: string; completedAt?: string; result?: SyncJobResult; error?: string; retryCount: number; } export interface GetJobStatusParams { jobId: string; } export interface GetJobHistoryParams { userDid: string; sliceUri: string; limit?: number; } export type GetJobHistoryResponse = JobStatus[]; export interface CollectionStats { collection: string; recordCount: number; uniqueActors: number; } export interface SliceStatsParams { slice: string; } export interface SliceStatsOutput { success: boolean; collections: string[]; collectionStats: CollectionStats[]; totalLexicons: number; totalRecords: number; totalActors: number; message?: string; } export interface SliceRecordsParams { slice: string; collection: string; repo?: string; limit?: number; cursor?: string; } export interface SliceRecordsOutput { success: boolean; records: IndexedRecord[]; cursor?: string; message?: string; } export interface UploadBlobRequest { data: ArrayBuffer | Uint8Array; mimeType: string; } export interface BlobRef { $type: string; ref: string; mimeType: string; size: number; } export interface UploadBlobResponse { blob: BlobRef; } export interface CollectionOperations { listRecords(params?: ListRecordsParams): Promise>; getRecord(params: GetRecordParams): Promise>; searchRecords(params: SearchRecordsParams): Promise>; } export interface AppBskyActorProfileRecord { /** Small image to be displayed next to posts from account. AKA, 'profile picture' */ avatar?: BlobRef; /** Larger horizontal image to display behind profile view. */ banner?: BlobRef; createdAt?: string; /** Free-form profile description text. */ description?: string; displayName?: string; } export type AppBskyActorProfileRecordSortFields = | "createdAt" | "description" | "displayName"; export interface XyzStatusphereStatusRecord { createdAt: string; status: string; } export type XyzStatusphereStatusRecordSortFields = "createdAt" | "status"; class BaseClient { protected readonly baseUrl: string; protected oauthClient?: OAuthClient; constructor(baseUrl: string, oauthClient?: OAuthClient) { this.baseUrl = baseUrl; this.oauthClient = oauthClient; } protected async ensureValidToken(): Promise { if (!this.oauthClient) { throw new Error("OAuth client not configured"); } await this.oauthClient.ensureValidToken(); } protected async makeRequest( endpoint: string, method?: "GET" | "POST" | "PUT" | "DELETE", params?: Record | unknown ): Promise { return this.makeRequestWithRetry(endpoint, method, params, false); } private async makeRequestWithRetry( endpoint: string, method?: "GET" | "POST" | "PUT" | "DELETE", params?: Record | unknown, isRetry?: boolean ): Promise { isRetry = isRetry ?? false; const httpMethod = method || "GET"; let url = `${this.baseUrl}/xrpc/${endpoint}`; const requestInit: RequestInit = { method: httpMethod, headers: {}, }; // Add authorization header if OAuth client is available if (this.oauthClient) { try { const tokens = await this.oauthClient.ensureValidToken(); if (tokens.accessToken) { (requestInit.headers as Record)[ "Authorization" ] = `${tokens.tokenType} ${tokens.accessToken}`; } } catch (tokenError) { // For write operations, OAuth tokens are required if (httpMethod !== "GET") { throw new Error( `Authentication required: OAuth tokens are invalid or expired. Please log in again.` ); } // For read operations, continue without auth (allow read-only operations) } } if (httpMethod === "GET" && params) { const searchParams = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== null) { searchParams.append(key, String(value)); } }); const queryString = searchParams.toString(); if (queryString) { url += "?" + queryString; } } else if (httpMethod !== "GET" && params) { // Regular API endpoints expect JSON (requestInit.headers as Record)["Content-Type"] = "application/json"; requestInit.body = JSON.stringify(params); } const response = await fetch(url, requestInit); if (!response.ok) { // Handle 404 gracefully for GET requests if (response.status === 404 && httpMethod === "GET") { return null as T; } // Handle 401 Unauthorized - attempt token refresh and retry once if ( response.status === 401 && !isRetry && this.oauthClient && httpMethod !== "GET" ) { try { // Force token refresh by calling ensureValidToken again await this.oauthClient.ensureValidToken(); // Retry the request once with refreshed tokens return this.makeRequestWithRetry(endpoint, method, params, true); } catch (_refreshError) { throw new Error( `Authentication required: OAuth tokens are invalid or expired. Please log in again.` ); } } throw new Error( `Request failed: ${response.status} ${response.statusText}` ); } return (await response.json()) as T; } } class ProfileActorBskyAppClient extends BaseClient { private readonly sliceUri: string; constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient) { super(baseUrl, oauthClient); this.sliceUri = sliceUri; } async listRecords( params?: ListRecordsParams ): Promise> { const requestParams = { ...params, slice: this.sliceUri }; return await this.makeRequest< ListRecordsResponse >("app.bsky.actor.profile.list", "GET", requestParams); } async getRecord( params: GetRecordParams ): Promise> { const requestParams = { ...params, slice: this.sliceUri }; return await this.makeRequest>( "app.bsky.actor.profile.get", "GET", requestParams ); } async searchRecords( params: SearchRecordsParams ): Promise> { const requestParams = { ...params, slice: this.sliceUri }; return await this.makeRequest< ListRecordsResponse >("app.bsky.actor.profile.searchRecords", "GET", requestParams); } async createRecord( record: AppBskyActorProfileRecord, useSelfRkey?: boolean ): Promise<{ uri: string; cid: string }> { const recordWithType = { $type: "app.bsky.actor.profile", ...record }; const payload = useSelfRkey ? { ...recordWithType, rkey: "self" } : recordWithType; return await this.makeRequest<{ uri: string; cid: string }>( "app.bsky.actor.profile.create", "POST", payload ); } async updateRecord( rkey: string, record: AppBskyActorProfileRecord ): Promise<{ uri: string; cid: string }> { const recordWithType = { $type: "app.bsky.actor.profile", ...record }; return await this.makeRequest<{ uri: string; cid: string }>( "app.bsky.actor.profile.update", "POST", { rkey, record: recordWithType } ); } async deleteRecord(rkey: string): Promise { return await this.makeRequest( "app.bsky.actor.profile.delete", "POST", { rkey } ); } } class ActorBskyAppClient extends BaseClient { readonly profile: ProfileActorBskyAppClient; private readonly sliceUri: string; constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient) { super(baseUrl, oauthClient); this.sliceUri = sliceUri; this.profile = new ProfileActorBskyAppClient( baseUrl, sliceUri, oauthClient ); } } class BskyAppClient extends BaseClient { readonly actor: ActorBskyAppClient; private readonly sliceUri: string; constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient) { super(baseUrl, oauthClient); this.sliceUri = sliceUri; this.actor = new ActorBskyAppClient(baseUrl, sliceUri, oauthClient); } } class AppClient extends BaseClient { readonly bsky: BskyAppClient; private readonly sliceUri: string; constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient) { super(baseUrl, oauthClient); this.sliceUri = sliceUri; this.bsky = new BskyAppClient(baseUrl, sliceUri, oauthClient); } } class StatusStatusphereXyzClient extends BaseClient { private readonly sliceUri: string; constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient) { super(baseUrl, oauthClient); this.sliceUri = sliceUri; } async listRecords( params?: ListRecordsParams ): Promise> { const requestParams = { ...params, slice: this.sliceUri }; return await this.makeRequest< ListRecordsResponse >("xyz.statusphere.status.list", "GET", requestParams); } async getRecord( params: GetRecordParams ): Promise> { const requestParams = { ...params, slice: this.sliceUri }; return await this.makeRequest>( "xyz.statusphere.status.get", "GET", requestParams ); } async searchRecords( params: SearchRecordsParams ): Promise> { const requestParams = { ...params, slice: this.sliceUri }; return await this.makeRequest< ListRecordsResponse >("xyz.statusphere.status.searchRecords", "GET", requestParams); } async createRecord( record: XyzStatusphereStatusRecord, useSelfRkey?: boolean ): Promise<{ uri: string; cid: string }> { const recordWithType = { $type: "xyz.statusphere.status", ...record }; const payload = useSelfRkey ? { ...recordWithType, rkey: "self" } : recordWithType; return await this.makeRequest<{ uri: string; cid: string }>( "xyz.statusphere.status.create", "POST", payload ); } async updateRecord( rkey: string, record: XyzStatusphereStatusRecord ): Promise<{ uri: string; cid: string }> { const recordWithType = { $type: "xyz.statusphere.status", ...record }; return await this.makeRequest<{ uri: string; cid: string }>( "xyz.statusphere.status.update", "POST", { rkey, record: recordWithType } ); } async deleteRecord(rkey: string): Promise { return await this.makeRequest( "xyz.statusphere.status.delete", "POST", { rkey } ); } } class StatusphereXyzClient extends BaseClient { readonly status: StatusStatusphereXyzClient; private readonly sliceUri: string; constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient) { super(baseUrl, oauthClient); this.sliceUri = sliceUri; this.status = new StatusStatusphereXyzClient( baseUrl, sliceUri, oauthClient ); } } class XyzClient extends BaseClient { readonly statusphere: StatusphereXyzClient; private readonly sliceUri: string; constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient) { super(baseUrl, oauthClient); this.sliceUri = sliceUri; this.statusphere = new StatusphereXyzClient(baseUrl, sliceUri, oauthClient); } } export class AtProtoClient extends BaseClient { readonly app: AppClient; readonly xyz: XyzClient; readonly oauth?: OAuthClient; private readonly sliceUri: string; constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient) { super(baseUrl, oauthClient); this.sliceUri = sliceUri; this.app = new AppClient(baseUrl, sliceUri, oauthClient); this.xyz = new XyzClient(baseUrl, sliceUri, oauthClient); this.oauth = this.oauthClient; } async getActors(params: GetActorsParams): Promise { const requestParams = { ...params, slice: this.sliceUri }; return await this.makeRequest( "social.slices.slice.getActors", "GET", requestParams ); } uploadBlob(request: UploadBlobRequest): Promise { return this.uploadBlobWithRetry(request, false); } private async uploadBlobWithRetry( request: UploadBlobRequest, isRetry?: boolean ): Promise { isRetry = isRetry ?? false; // Special handling for blob upload with binary data const httpMethod = "POST"; const url = `${this.baseUrl}/xrpc/com.atproto.repo.uploadBlob`; if (!this.oauthClient) { throw new Error("OAuth client not configured"); } const tokens = await this.oauthClient.ensureValidToken(); const requestInit: RequestInit = { method: httpMethod, headers: { "Content-Type": request.mimeType, Authorization: `${tokens.tokenType} ${tokens.accessToken}`, }, body: request.data, }; const response = await fetch(url, requestInit); if (!response.ok) { // Handle 401 Unauthorized - attempt token refresh and retry once if (response.status === 401 && !isRetry && this.oauthClient) { try { // Force token refresh by calling ensureValidToken again await this.oauthClient.ensureValidToken(); // Retry the request once with refreshed tokens return this.uploadBlobWithRetry(request, true); } catch (_refreshError) { throw new Error( `Authentication required: OAuth tokens are invalid or expired. Please log in again.` ); } } throw new Error( `Blob upload failed: ${response.status} ${response.statusText}` ); } return await response.json(); } }