UNPKG

@crosspost/sdk

Version:

SDK for interacting with the Crosspost API

412 lines (346 loc) 12.1 kB
# @crosspost/sdk SDK for interacting with the [Crosspost Proxy Service](./../../README.md). This package is designed to be used with [near-sign-verify](https://github.com/elliotBraem/near-sign-verify) for authenticating requests via a wallet or keypair. ## Installation ```bash bun install @crosspost/sdk ``` ## Usage ```typescript import * as near from "fastintear"; // or near-api-js for creating key pairs import { sign } from "near-sign-verify"; import { CrosspostClient, // error handling helpers CrosspostError, isAuthError, isPlatformError, isRateLimitError, isValidationError } from '@crosspost/sdk'; import type { ConnectedAccount, CreatePostRequest, } from "@crosspost/sdk"; // Initialize the client const client = new CrosspostClient({ baseUrl: 'https://your-self-hosted-crosspost-api.com', // Optional: Defaults to official API }); const authToken = await sign({ signer: near, recipient: "crosspost.near", message: "createPost" }); client.setAuthentication(authToken); client.setAccountHeader("signer.near") const connectedAccounts: ApiResponse<ConnectedAccountsResponse> = await client.auth.getConnectedAccounts(): try { const response = await await client.post.createPost({ targets: [ { userId: connectedAccounts[0].userId, platform: connectedAccounts[0].platform } ], content: [{ text: "hello world", media: { data: imageBlob, mimeType: 'image/jpeg', altText: 'a beautiful sunset', } }] } as CreatePostRequest); console.log('Post created successfully'); console.log('Post ID:', response.id); console.log('Platform:', response.platform); console.log('URL:', response.url); console.log('Created at:', response.createdAt); } catch (error) { // Check if it's an authentication error if (isAuthError(error)) { console.error('Authentication required. Attempting to authorize...'); // The account must be authorized with the backend const authorized = await client.auth.authorizeNearAccount(); if (authorized) { // Retry the operation return createPost(); } } else { // Handle other error types console.error('Error creating post:', error); if (error instanceof CrosspostError) { // Use error utility functions to handle specific cases if (isPlatformError(error)) { console.error('Platform:', error.platform); console.error('Error code:', error.code); console.error('Details:', error.details); } else if (isRateLimitError(error)) { console.error('Rate limited until:', error.details?.rateLimit?.reset); } else if (isValidationError(error)) { console.error('Validation errors:', error.details?.validationErrors); } // Check if error is recoverable if (error.recoverable) { console.log('This error is recoverable - retry may succeed'); } } else if (error instanceof Error) { // Handle non-API errors (network issues, etc) console.error('Unexpected error:', error.message); } } } ``` ## Methods - `client.setAuthentication(authToken: string): Promise<void>` - Sets authentication data, necessary for non-GET requests - `client.isAuthenticated(): boolean` - Checks if client is authenticated - `client.setAccountHeader(accountId: string): Promise<void>` - Sets X-Near-Account Header, necessary for GET requests - `client.clear(): boolean` - Clears authentication and account header ### Auth API (client.auth) - `client.auth.authorizeNearAccount(): Promise<ApiResponse<NearAuthorizationResponse>>` - Authorizes NEAR account - `client.auth.unauthorizeNearAccount(): Promise<ApiResponse<NearAuthorizationResponse>>` - Unauthorizes NEAR account - `client.auth.getNearAuthorizationStatus(): Promise<ApiResponse<NearAuthorizationResponse>>` - Checks authorization status for authenticated account - `client.auth.loginToPlatform(platform, options?): Promise<AuthCallbackResponse | ApiResponse<AuthUrlResponse>>` - Opens popup to initiate OAuth flow with platform - `client.auth.refreshToken(platform): Promise<ApiResponse<AuthCallbackResponse>>` - Refreshes platform token - `client.auth.refreshProfile(platform): Promise<ApiResponse<ConnectedAccount>>` - Refreshes user profile - `client.auth.getAuthStatus(platform): Promise<ApiResponse<AuthStatusResponse>>` - Gets authentication status - `client.auth.revokeAuth(platform): Promise<ApiResponse<AuthRevokeResponse>>` - Revokes platform access - `client.auth.getConnectedAccounts(): Promise<ApiResponse<ConnectedAccountsResponse>>` - Lists connected accounts ### Post API (client.post) Each post operation accepts a request object that includes: - `targets`: Array of `{ platform: string, userId: string }` specifying where to perform the action - Additional parameters specific to each operation Available methods: - `client.post.createPost(request: CreatePostRequest): Promise<CreatePostResponse>` - Creates posts on specified platforms - `client.post.repost(request: RepostRequest): Promise<RepostResponse>` - Reposts an existing post - `client.post.quotePost(request: QuotePostRequest): Promise<QuotePostResponse>` - Quotes an existing post - `client.post.replyToPost(request: ReplyToPostRequest): Promise<ReplyToPostResponse>` - Replies to a post - `client.post.likePost(request: LikePostRequest): Promise<LikePostResponse>` - Likes a post - `client.post.unlikePost(request: UnlikePostRequest): Promise<UnlikePostResponse>` - Unlikes a post - `client.post.deletePost(request: DeletePostRequest): Promise<DeletePostResponse>` - Deletes posts ### Activity API (client.activity) - `client.activity.getLeaderboard(options): Promise<LeaderboardResponse>` - Gets activity leaderboard - `client.activity.getAccountActivity(signerId, options): Promise<AccountActivityResponse>` - Gets account activity - `client.activity.getAccountPosts(signerId, options): Promise<AccountPostsResponse>` - Gets account posts ### System API (client.system) - `client.system.getRateLimits(): Promise<RateLimitsResponse>` - Gets all rate limits - `client.system.getEndpointRateLimit(endpoint): Promise<EndpointRateLimitResponse>` - Gets endpoint rate limit - `client.system.getHealthStatus(): Promise<HealthStatusResponse>` - Gets API health status ## API Reference ### Pagination The SDK supports offset-based pagination for endpoints that return large collections: ```typescript // Get paginated results with specific limit and offset const response = await client.activity.getLeaderboard({ limit: 10, // Number of items per page offset: 20, // Skip the first 20 items }); // Access pagination metadata console.log(`Total items: ${response.meta.pagination?.total}`); console.log(`Current page size: ${response.meta.pagination?.limit}`); console.log(`Current offset: ${response.meta.pagination?.offset}`); ``` ### Multi-Status Responses Post operations always return multi-status responses: ```typescript // Operation targeting multiple platforms const response = await client.post.createPost({ targets: [ { platform: 'twitter', userId: 'user1' }, { platform: 'facebook', userId: 'user2' }, ], content: [{ text: 'hello world' }], }); // Check multi-status summary console.log(`Total operations: ${response.data.summary.total}`); console.log(`Successful: ${response.data.summary.succeeded}`); console.log(`Failed: ${response.data.summary.failed}`); // Access successful results response.data.results.forEach((result) => { console.log(`Success on ${result.platform}: ${result.details.id}`); }); // Access errors (if any) if (response.data.errors && response.data.errors.length > 0) { // Error structure is identical to error.details.errors when all operations fail response.data.errors.forEach((error) => { console.log(`Error on ${error.details.platform}: ${error.message}`); console.log(`Error code: ${error.code}`); console.log(`Recoverable: ${error.recoverable}`); }); } ``` If all operations fail, the SDK throws a `CrosspostError` with the same error structure in `details.errors`: ```typescript try { await client.post.createPost({...}); } catch (error) { if (error instanceof CrosspostError) { // Error structure is identical to response.data.errors in partial success case error.details.errors.forEach(err => { console.log(`Error on ${err.details.platform}: ${err.message}`); console.log(`Error code: ${err.code}`); console.log(`Recoverable: ${err.recoverable}`); }); } } ``` This consistent error structure allows you to use the same error handling logic regardless of whether you're dealing with partial failures in a multi-status response or complete failure. ### Validation Error Handling The SDK provides detailed validation error information: ```typescript try { await client.post.createPost({ // Invalid or missing required fields }); } catch (error) { if (isValidationError(error)) { console.error('Validation failed:'); // Access validation error details if (error.details?.validationErrors) { Object.entries(error.details.validationErrors).forEach(([field, issues]) => { console.error(`Field '${field}': ${issues.join(', ')}`); }); } } } ``` ### Error Handling Utilities ```typescript import { getErrorDetails, getErrorMessage, isAuthError, isContentError, isMediaError, isNetworkError, isPlatformError, isPostError, isRateLimitError, isRecoverableError, isValidationError, } from '@crosspost/sdk'; // Check error types if (isAuthError(error)) { // Handle authentication errors } // Get user-friendly error message const message = getErrorMessage(error, 'Default message'); // Get error details const details = getErrorDetails(error); ``` ## Usage Examples ### Creating a Post ```typescript // Create a text post on Twitter const textPostResponse = await client.post.createPost({ targets: [{ platform: 'twitter', userId: 'your-twitter-id', }], content: [{ text: 'Hello from Crosspost SDK!', }], }); // Create a post with media on multiple platforms const mediaPostResponse = await client.post.createPost({ targets: [ { platform: 'twitter', userId: 'your-twitter-id' }, { platform: 'facebook', userId: 'your-facebook-id' }, ], content: [{ text: 'Check out this image!', media: [{ data: imageBlob, mimeType: 'image/jpeg', altText: 'A beautiful sunset', }], }], }); ``` ### Post Interactions ```typescript // Like a post on Twitter await client.post.likePost({ targets: [{ platform: 'twitter', userId: 'your-twitter-id', }], platform: 'twitter', postId: '1234567890', }); // Repost on multiple platforms await client.post.repost({ targets: [ { platform: 'twitter', userId: 'your-twitter-id' }, { platform: 'facebook', userId: 'your-facebook-id' }, ], platform: 'twitter', postId: '1234567890', }); // Reply to a post await client.post.replyToPost({ targets: [{ platform: 'twitter', userId: 'your-twitter-id', }], platform: 'twitter', postId: '1234567890', content: [{ text: 'This is a reply', }], }); // Delete posts await client.post.deletePost({ targets: [{ platform: 'twitter', userId: 'your-twitter-id', }], posts: [{ platform: 'twitter', userId: 'your-twitter-id', postId: '1234567890', }], }); ``` ### Getting Activity Data ```typescript // Get leaderboard const leaderboard = await client.activity.getLeaderboard({ timeframe: 'week', limit: 10, }); // Get account activity const activity = await client.activity.getAccountActivity('user.near', { timeframe: 'month', }); // Get account posts const posts = await client.activity.getAccountPosts('user.near', { limit: 20, offset: 0, }); ``` ### Checking Rate Limits ```typescript // Get all rate limits const rateLimits = await client.system.getRateLimits(); // Get rate limit for a specific endpoint const postRateLimit = await client.system.getEndpointRateLimit('post'); ```