@crosspost/sdk 0.1.14 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -147,6 +147,103 @@ async function createPost() {
147
147
 
148
148
  ## API Reference
149
149
 
150
+ ### Pagination
151
+
152
+ The SDK supports offset-based pagination for endpoints that return large collections:
153
+
154
+ ```typescript
155
+ // Get paginated results with specific limit and offset
156
+ const response = await client.activity.getLeaderboard({
157
+ limit: 10, // Number of items per page
158
+ offset: 20, // Skip the first 20 items
159
+ });
160
+
161
+ // Access pagination metadata
162
+ console.log(`Total items: ${response.meta.pagination?.total}`);
163
+ console.log(`Current page size: ${response.meta.pagination?.limit}`);
164
+ console.log(`Current offset: ${response.meta.pagination?.offset}`);
165
+ ```
166
+
167
+ ### Multi-Status Responses
168
+
169
+ Some operations that target multiple platforms may result in partial success. The SDK handles these
170
+ cases with multi-status responses:
171
+
172
+ ```typescript
173
+ // Operation targeting multiple platforms
174
+ const response = await client.post.createPost({
175
+ targets: [
176
+ { platform: 'twitter', userId: 'user1' },
177
+ { platform: 'facebook', userId: 'user2' },
178
+ ],
179
+ content: [{ text: 'Hello world!' }],
180
+ });
181
+
182
+ // Check multi-status summary
183
+ console.log(`Total operations: ${response.data.summary.total}`);
184
+ console.log(`Successful: ${response.data.summary.succeeded}`);
185
+ console.log(`Failed: ${response.data.summary.failed}`);
186
+
187
+ // Access successful results
188
+ response.data.results.forEach((result) => {
189
+ console.log(`Success on ${result.platform}: ${result.details.id}`);
190
+ });
191
+
192
+ // Access errors (if any)
193
+ if (response.data.errors && response.data.errors.length > 0) {
194
+ // Error structure is identical to error.details.errors when all operations fail
195
+ response.data.errors.forEach((error) => {
196
+ console.log(`Error on ${error.details.platform}: ${error.message}`);
197
+ console.log(`Error code: ${error.code}`);
198
+ console.log(`Recoverable: ${error.recoverable}`);
199
+ });
200
+ }
201
+ ```
202
+
203
+ If all operations fail, the SDK throws a `CrosspostError` with the same error structure in
204
+ `details.errors`:
205
+
206
+ ```typescript
207
+ try {
208
+ await client.post.createPost({...});
209
+ } catch (error) {
210
+ if (error instanceof CrosspostError && error.details?.errors) {
211
+ // Error structure is identical to response.data.errors in partial success case
212
+ error.details.errors.forEach(err => {
213
+ console.log(`Error on ${err.details.platform}: ${err.message}`);
214
+ console.log(`Error code: ${err.code}`);
215
+ console.log(`Recoverable: ${err.recoverable}`);
216
+ });
217
+ }
218
+ }
219
+ ```
220
+
221
+ This consistent error structure allows you to use the same error handling logic regardless of
222
+ whether you're dealing with partial failures in a multi-status response or complete failure.
223
+
224
+ ### Validation Error Handling
225
+
226
+ The SDK provides detailed validation error information:
227
+
228
+ ```typescript
229
+ try {
230
+ await client.post.createPost({
231
+ // Invalid or missing required fields
232
+ });
233
+ } catch (error) {
234
+ if (isValidationError(error)) {
235
+ console.error('Validation failed:');
236
+
237
+ // Access validation error details
238
+ if (error.details?.validationErrors) {
239
+ Object.entries(error.details.validationErrors).forEach(([field, issues]) => {
240
+ console.error(`Field '${field}': ${issues.join(', ')}`);
241
+ });
242
+ }
243
+ }
244
+ }
245
+ ```
246
+
150
247
  ### CrosspostClient
151
248
 
152
249
  ```typescript
package/dist/index.cjs CHANGED
@@ -265,18 +265,13 @@ function createNetworkError(error, url, timeout) {
265
265
 
266
266
  // src/core/request.ts
267
267
  async function makeRequest(method, path, options, data, query) {
268
- let url = `${options.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
268
+ const url = new URL(path, options.baseUrl);
269
269
  if (query && typeof query === "object" && Object.keys(query).length > 0) {
270
- const queryParams = new URLSearchParams();
271
270
  for (const [key, value] of Object.entries(query)) {
272
271
  if (value !== void 0 && value !== null) {
273
- queryParams.append(key, String(value));
272
+ url.searchParams.append(key, String(value));
274
273
  }
275
274
  }
276
- const queryString = queryParams.toString();
277
- if (queryString) {
278
- url += `?${queryString}`;
279
- }
280
275
  }
281
276
  const context = {
282
277
  method,
@@ -341,24 +336,16 @@ async function makeRequest(method, path, options, data, query) {
341
336
  if (!response.ok) {
342
337
  throw handleErrorResponse(responseData, response.status);
343
338
  }
344
- if (!responseData || typeof responseData !== "object" || !("success" in responseData)) {
339
+ if (!responseData || typeof responseData !== "object" || !("success" in responseData) || !("meta" in responseData)) {
345
340
  throw new CrosspostError(
346
- "Invalid success response format from API",
341
+ "Invalid response format from API",
347
342
  import_types2.ApiErrorCode.INVALID_RESPONSE,
348
343
  response.status,
349
344
  { responseData }
350
345
  );
351
346
  }
352
347
  if (responseData.success) {
353
- if (!responseData.data) {
354
- throw new CrosspostError(
355
- "API returned success but no data",
356
- import_types2.ApiErrorCode.INVALID_RESPONSE,
357
- response.status,
358
- { responseData }
359
- );
360
- }
361
- return responseData.data;
348
+ return responseData;
362
349
  }
363
350
  throw handleErrorResponse(responseData, response.status);
364
351
  } catch (error) {
@@ -367,7 +354,10 @@ async function makeRequest(method, path, options, data, query) {
367
354
  throw enrichErrorWithContext(error, context);
368
355
  }
369
356
  if (error instanceof TypeError || error instanceof DOMException && error.name === "AbortError") {
370
- throw enrichErrorWithContext(createNetworkError(error, url, options.timeout), context);
357
+ throw enrichErrorWithContext(
358
+ createNetworkError(error, url.toString(), options.timeout),
359
+ context
360
+ );
371
361
  }
372
362
  throw enrichErrorWithContext(
373
363
  new CrosspostError(
@@ -472,15 +462,6 @@ function openAuthPopup(url, options = {}) {
472
462
  } else {
473
463
  reject(message.data);
474
464
  }
475
- setTimeout(() => {
476
- try {
477
- if (popup && !popup.closed) {
478
- popup.close();
479
- }
480
- } catch (e) {
481
- console.warn("Failed to close popup window:", e);
482
- }
483
- }, 100);
484
465
  }
485
466
  };
486
467
  window.addEventListener("message", handleMessage);
@@ -552,13 +533,20 @@ var AuthApi = class {
552
533
  * @throws Error if popups are blocked or if running in a non-browser environment.
553
534
  */
554
535
  async loginToPlatform(platform, options) {
555
- const { url } = await makeRequest(
536
+ const requestOptions = options || { redirect: false };
537
+ const response = await makeRequest(
556
538
  "POST",
557
539
  `/auth/${platform}/login`,
558
540
  this.options,
559
- options || { redirect: false }
541
+ requestOptions
560
542
  );
561
- const result = await openAuthPopup(url);
543
+ if (requestOptions.redirect) {
544
+ return response;
545
+ }
546
+ if (!response.data || !("url" in response.data)) {
547
+ throw new Error("Invalid authentication URL response");
548
+ }
549
+ const result = await openAuthPopup(response.data.url);
562
550
  if (!result.success || !result.userId) {
563
551
  throw new Error(result.error || "Authentication failed");
564
552
  }
@@ -799,7 +787,7 @@ var SystemApi = class {
799
787
 
800
788
  // src/core/config.ts
801
789
  var DEFAULT_CONFIG = {
802
- baseUrl: "https://open-crosspost-proxy.deno.dev/",
790
+ baseUrl: new URL("https://open-crosspost-proxy.deno.dev/"),
803
791
  timeout: 3e4
804
792
  };
805
793
 
@@ -814,7 +802,7 @@ var CrosspostClient = class {
814
802
  const timeout = config.timeout || DEFAULT_CONFIG.timeout;
815
803
  const nearAuthData = config.nearAuthData;
816
804
  this.options = {
817
- baseUrl,
805
+ baseUrl: baseUrl instanceof URL ? baseUrl : new URL(baseUrl),
818
806
  timeout,
819
807
  nearAuthData
820
808
  };
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { NearAuthData } from 'near-sign-verify';
2
- import { ActivityLeaderboardQuery, ActivityLeaderboardResponse, AccountActivityQuery, AccountActivityResponse, AccountPostsQuery, AccountPostsResponse, NearAuthorizationResponse, Platform, AuthInitRequest, AuthCallbackResponse, ConnectedAccount, AuthStatusResponse, NearUnauthorizationResponse, AuthRevokeResponse, ConnectedAccountsResponse, CreatePostRequest, CreatePostResponse, RepostRequest, RepostResponse, QuotePostRequest, QuotePostResponse, ReplyToPostRequest, ReplyToPostResponse, LikePostRequest, LikePostResponse, UnlikePostRequest, UnlikePostResponse, DeletePostRequest, DeletePostResponse, RateLimitResponse, EndpointRateLimitResponse, HealthStatus, ApiErrorCode, StatusCode, ErrorDetails } from '@crosspost/types';
2
+ import { ActivityLeaderboardQuery, ApiResponse, ActivityLeaderboardResponse, AccountActivityQuery, AccountActivityResponse, AccountPostsQuery, AccountPostsResponse, NearAuthorizationResponse, Platform, AuthInitRequest, AuthCallbackResponse, AuthUrlResponse, ConnectedAccount, AuthStatusResponse, NearUnauthorizationResponse, AuthRevokeResponse, ConnectedAccountsResponse, CreatePostRequest, CreatePostResponse, RepostRequest, RepostResponse, QuotePostRequest, QuotePostResponse, ReplyToPostRequest, ReplyToPostResponse, LikePostRequest, LikePostResponse, UnlikePostRequest, UnlikePostResponse, DeletePostRequest, DeletePostResponse, RateLimitResponse, EndpointRateLimitResponse, HealthStatus, ApiErrorCode, StatusCode, ErrorDetails } from '@crosspost/types';
3
3
  export * from '@crosspost/types';
4
4
 
5
5
  /**
@@ -9,7 +9,7 @@ interface RequestOptions {
9
9
  /**
10
10
  * Base URL for the API
11
11
  */
12
- baseUrl: string;
12
+ baseUrl: URL;
13
13
  /**
14
14
  * NEAR authentication data for generating auth tokens
15
15
  * Required for non-GET requests, optional for GET requests
@@ -41,21 +41,21 @@ declare class ActivityApi {
41
41
  * @param query Optional query parameters
42
42
  * @returns A promise resolving with the leaderboard response
43
43
  */
44
- getLeaderboard(query?: ActivityLeaderboardQuery): Promise<ActivityLeaderboardResponse>;
44
+ getLeaderboard(query?: ActivityLeaderboardQuery): Promise<ApiResponse<ActivityLeaderboardResponse>>;
45
45
  /**
46
46
  * Gets activity for a specific account
47
47
  * @param signerId The NEAR account ID
48
48
  * @param query Optional query parameters
49
49
  * @returns A promise resolving with the account activity response
50
50
  */
51
- getAccountActivity(signerId: string, query?: AccountActivityQuery): Promise<AccountActivityResponse>;
51
+ getAccountActivity(signerId: string, query?: AccountActivityQuery): Promise<ApiResponse<AccountActivityResponse>>;
52
52
  /**
53
53
  * Gets posts for a specific account
54
54
  * @param signerId The NEAR account ID
55
55
  * @param query Optional query parameters
56
56
  * @returns A promise resolving with the account posts response
57
57
  */
58
- getAccountPosts(signerId: string, query?: AccountPostsQuery): Promise<AccountPostsResponse>;
58
+ getAccountPosts(signerId: string, query?: AccountPostsQuery): Promise<ApiResponse<AccountPostsResponse>>;
59
59
  }
60
60
 
61
61
  /**
@@ -72,12 +72,12 @@ declare class AuthApi {
72
72
  * Authorizes the NEAR account associated with the provided nearAuthData with the Crosspost service.
73
73
  * @returns A promise resolving with the authorization response.
74
74
  */
75
- authorizeNearAccount(): Promise<NearAuthorizationResponse>;
75
+ authorizeNearAccount(): Promise<ApiResponse<NearAuthorizationResponse>>;
76
76
  /**
77
77
  * Checks the authorization status of the NEAR account with the Crosspost service.
78
78
  * @returns A promise resolving with the authorization status response.
79
79
  */
80
- getNearAuthorizationStatus(): Promise<NearAuthorizationResponse>;
80
+ getNearAuthorizationStatus(): Promise<ApiResponse<NearAuthorizationResponse>>;
81
81
  /**
82
82
  * Initiates the login process for a specific platform using a popup window.
83
83
  * @param platform The target platform.
@@ -85,43 +85,43 @@ declare class AuthApi {
85
85
  * @returns Promise that resolves with the authentication result when the popup completes.
86
86
  * @throws Error if popups are blocked or if running in a non-browser environment.
87
87
  */
88
- loginToPlatform(platform: Platform, options?: AuthInitRequest): Promise<AuthCallbackResponse>;
88
+ loginToPlatform(platform: Platform, options?: AuthInitRequest): Promise<AuthCallbackResponse | ApiResponse<AuthUrlResponse>>;
89
89
  /**
90
90
  * Refreshes the authentication token for the specified platform.
91
91
  * @param platform The target platform.
92
92
  * @returns A promise resolving with the refresh response containing updated auth details.
93
93
  */
94
- refreshToken(platform: Platform, userId: string): Promise<AuthCallbackResponse>;
94
+ refreshToken(platform: Platform, userId: string): Promise<ApiResponse<AuthCallbackResponse>>;
95
95
  /**
96
96
  * Refreshes the user's profile information from the specified platform.
97
97
  * @param platform The target platform.
98
98
  * @param userId The user ID on the platform
99
99
  * @returns A promise resolving with the updated account profile information.
100
100
  */
101
- refreshProfile(platform: Platform, userId: string): Promise<ConnectedAccount>;
101
+ refreshProfile(platform: Platform, userId: string): Promise<ApiResponse<ConnectedAccount>>;
102
102
  /**
103
103
  * Gets the authentication status for the specified platform.
104
104
  * @param platform The target platform.
105
105
  * @returns A promise resolving with the authentication status response.
106
106
  */
107
- getAuthStatus(platform: Platform, userId: string): Promise<AuthStatusResponse>;
107
+ getAuthStatus(platform: Platform, userId: string): Promise<ApiResponse<AuthStatusResponse>>;
108
108
  /**
109
109
  * Unauthorizes a NEAR account from using the service
110
110
  * @returns A promise resolving with the unauthorized response
111
111
  */
112
- unauthorizeNear(): Promise<NearUnauthorizationResponse>;
112
+ unauthorizeNear(): Promise<ApiResponse<NearUnauthorizationResponse>>;
113
113
  /**
114
114
  * Revokes the authentication token for the specified platform.
115
115
  * @param platform The target platform.
116
116
  * @returns A promise resolving with the revocation response.
117
117
  */
118
- revokeAuth(platform: Platform, userId: string): Promise<AuthRevokeResponse>;
118
+ revokeAuth(platform: Platform, userId: string): Promise<ApiResponse<AuthRevokeResponse>>;
119
119
  /**
120
120
  * Lists all accounts connected to the NEAR account.
121
121
  * @returns A promise resolving with the connected accounts response containing an array of accounts.
122
122
  * @throws {CrosspostError} If the request fails or returns invalid data.
123
123
  */
124
- getConnectedAccounts(): Promise<ConnectedAccountsResponse>;
124
+ getConnectedAccounts(): Promise<ApiResponse<ConnectedAccountsResponse>>;
125
125
  }
126
126
 
127
127
  /**
@@ -139,43 +139,43 @@ declare class PostApi {
139
139
  * @param request The post creation request details.
140
140
  * @returns A promise resolving with the post creation response.
141
141
  */
142
- createPost(request: CreatePostRequest): Promise<CreatePostResponse>;
142
+ createPost(request: CreatePostRequest): Promise<ApiResponse<CreatePostResponse>>;
143
143
  /**
144
144
  * Reposts an existing post on the specified target platforms.
145
145
  * @param request The repost request details.
146
146
  * @returns A promise resolving with the repost response.
147
147
  */
148
- repost(request: RepostRequest): Promise<RepostResponse>;
148
+ repost(request: RepostRequest): Promise<ApiResponse<RepostResponse>>;
149
149
  /**
150
150
  * Quotes an existing post on the specified target platforms.
151
151
  * @param request The quote post request details.
152
152
  * @returns A promise resolving with the quote post response.
153
153
  */
154
- quotePost(request: QuotePostRequest): Promise<QuotePostResponse>;
154
+ quotePost(request: QuotePostRequest): Promise<ApiResponse<QuotePostResponse>>;
155
155
  /**
156
156
  * Replies to an existing post on the specified target platforms.
157
157
  * @param request The reply request details.
158
158
  * @returns A promise resolving with the reply response.
159
159
  */
160
- replyToPost(request: ReplyToPostRequest): Promise<ReplyToPostResponse>;
160
+ replyToPost(request: ReplyToPostRequest): Promise<ApiResponse<ReplyToPostResponse>>;
161
161
  /**
162
162
  * Likes a post on the specified target platforms.
163
163
  * @param request The like request details.
164
164
  * @returns A promise resolving with the like response.
165
165
  */
166
- likePost(request: LikePostRequest): Promise<LikePostResponse>;
166
+ likePost(request: LikePostRequest): Promise<ApiResponse<LikePostResponse>>;
167
167
  /**
168
168
  * Unlikes a post on the specified target platforms.
169
169
  * @param request The unlike request details.
170
170
  * @returns A promise resolving with the unlike response.
171
171
  */
172
- unlikePost(request: UnlikePostRequest): Promise<UnlikePostResponse>;
172
+ unlikePost(request: UnlikePostRequest): Promise<ApiResponse<UnlikePostResponse>>;
173
173
  /**
174
174
  * Deletes one or more posts.
175
175
  * @param request The delete request details.
176
176
  * @returns A promise resolving with the delete response.
177
177
  */
178
- deletePost(request: DeletePostRequest): Promise<DeletePostResponse>;
178
+ deletePost(request: DeletePostRequest): Promise<ApiResponse<DeletePostResponse>>;
179
179
  }
180
180
 
181
181
  /**
@@ -193,18 +193,18 @@ declare class SystemApi {
193
193
  * Gets the current rate limit status
194
194
  * @returns A promise resolving with the rate limit response
195
195
  */
196
- getRateLimits(): Promise<RateLimitResponse>;
196
+ getRateLimits(): Promise<ApiResponse<RateLimitResponse>>;
197
197
  /**
198
198
  * Gets the rate limit status for a specific endpoint
199
199
  * @param endpoint The endpoint to get rate limit for
200
200
  * @returns A promise resolving with the endpoint rate limit response
201
201
  */
202
- getEndpointRateLimit(endpoint: string): Promise<EndpointRateLimitResponse>;
202
+ getEndpointRateLimit(endpoint: string): Promise<ApiResponse<EndpointRateLimitResponse>>;
203
203
  /**
204
204
  * Gets the health status of the API
205
205
  * @returns A promise resolving with the health status
206
206
  */
207
- getHealthStatus(): Promise<HealthStatus>;
207
+ getHealthStatus(): Promise<ApiResponse<HealthStatus>>;
208
208
  }
209
209
 
210
210
  /**
@@ -215,7 +215,7 @@ interface CrosspostClientConfig {
215
215
  * Base URL for the Crosspost API
216
216
  * @default 'https://open-crosspost-proxy.deno.dev'
217
217
  */
218
- baseUrl?: string;
218
+ baseUrl?: string | URL;
219
219
  /**
220
220
  * NEAR authentication data obtained from near-sign-verify
221
221
  */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { NearAuthData } from 'near-sign-verify';
2
- import { ActivityLeaderboardQuery, ActivityLeaderboardResponse, AccountActivityQuery, AccountActivityResponse, AccountPostsQuery, AccountPostsResponse, NearAuthorizationResponse, Platform, AuthInitRequest, AuthCallbackResponse, ConnectedAccount, AuthStatusResponse, NearUnauthorizationResponse, AuthRevokeResponse, ConnectedAccountsResponse, CreatePostRequest, CreatePostResponse, RepostRequest, RepostResponse, QuotePostRequest, QuotePostResponse, ReplyToPostRequest, ReplyToPostResponse, LikePostRequest, LikePostResponse, UnlikePostRequest, UnlikePostResponse, DeletePostRequest, DeletePostResponse, RateLimitResponse, EndpointRateLimitResponse, HealthStatus, ApiErrorCode, StatusCode, ErrorDetails } from '@crosspost/types';
2
+ import { ActivityLeaderboardQuery, ApiResponse, ActivityLeaderboardResponse, AccountActivityQuery, AccountActivityResponse, AccountPostsQuery, AccountPostsResponse, NearAuthorizationResponse, Platform, AuthInitRequest, AuthCallbackResponse, AuthUrlResponse, ConnectedAccount, AuthStatusResponse, NearUnauthorizationResponse, AuthRevokeResponse, ConnectedAccountsResponse, CreatePostRequest, CreatePostResponse, RepostRequest, RepostResponse, QuotePostRequest, QuotePostResponse, ReplyToPostRequest, ReplyToPostResponse, LikePostRequest, LikePostResponse, UnlikePostRequest, UnlikePostResponse, DeletePostRequest, DeletePostResponse, RateLimitResponse, EndpointRateLimitResponse, HealthStatus, ApiErrorCode, StatusCode, ErrorDetails } from '@crosspost/types';
3
3
  export * from '@crosspost/types';
4
4
 
5
5
  /**
@@ -9,7 +9,7 @@ interface RequestOptions {
9
9
  /**
10
10
  * Base URL for the API
11
11
  */
12
- baseUrl: string;
12
+ baseUrl: URL;
13
13
  /**
14
14
  * NEAR authentication data for generating auth tokens
15
15
  * Required for non-GET requests, optional for GET requests
@@ -41,21 +41,21 @@ declare class ActivityApi {
41
41
  * @param query Optional query parameters
42
42
  * @returns A promise resolving with the leaderboard response
43
43
  */
44
- getLeaderboard(query?: ActivityLeaderboardQuery): Promise<ActivityLeaderboardResponse>;
44
+ getLeaderboard(query?: ActivityLeaderboardQuery): Promise<ApiResponse<ActivityLeaderboardResponse>>;
45
45
  /**
46
46
  * Gets activity for a specific account
47
47
  * @param signerId The NEAR account ID
48
48
  * @param query Optional query parameters
49
49
  * @returns A promise resolving with the account activity response
50
50
  */
51
- getAccountActivity(signerId: string, query?: AccountActivityQuery): Promise<AccountActivityResponse>;
51
+ getAccountActivity(signerId: string, query?: AccountActivityQuery): Promise<ApiResponse<AccountActivityResponse>>;
52
52
  /**
53
53
  * Gets posts for a specific account
54
54
  * @param signerId The NEAR account ID
55
55
  * @param query Optional query parameters
56
56
  * @returns A promise resolving with the account posts response
57
57
  */
58
- getAccountPosts(signerId: string, query?: AccountPostsQuery): Promise<AccountPostsResponse>;
58
+ getAccountPosts(signerId: string, query?: AccountPostsQuery): Promise<ApiResponse<AccountPostsResponse>>;
59
59
  }
60
60
 
61
61
  /**
@@ -72,12 +72,12 @@ declare class AuthApi {
72
72
  * Authorizes the NEAR account associated with the provided nearAuthData with the Crosspost service.
73
73
  * @returns A promise resolving with the authorization response.
74
74
  */
75
- authorizeNearAccount(): Promise<NearAuthorizationResponse>;
75
+ authorizeNearAccount(): Promise<ApiResponse<NearAuthorizationResponse>>;
76
76
  /**
77
77
  * Checks the authorization status of the NEAR account with the Crosspost service.
78
78
  * @returns A promise resolving with the authorization status response.
79
79
  */
80
- getNearAuthorizationStatus(): Promise<NearAuthorizationResponse>;
80
+ getNearAuthorizationStatus(): Promise<ApiResponse<NearAuthorizationResponse>>;
81
81
  /**
82
82
  * Initiates the login process for a specific platform using a popup window.
83
83
  * @param platform The target platform.
@@ -85,43 +85,43 @@ declare class AuthApi {
85
85
  * @returns Promise that resolves with the authentication result when the popup completes.
86
86
  * @throws Error if popups are blocked or if running in a non-browser environment.
87
87
  */
88
- loginToPlatform(platform: Platform, options?: AuthInitRequest): Promise<AuthCallbackResponse>;
88
+ loginToPlatform(platform: Platform, options?: AuthInitRequest): Promise<AuthCallbackResponse | ApiResponse<AuthUrlResponse>>;
89
89
  /**
90
90
  * Refreshes the authentication token for the specified platform.
91
91
  * @param platform The target platform.
92
92
  * @returns A promise resolving with the refresh response containing updated auth details.
93
93
  */
94
- refreshToken(platform: Platform, userId: string): Promise<AuthCallbackResponse>;
94
+ refreshToken(platform: Platform, userId: string): Promise<ApiResponse<AuthCallbackResponse>>;
95
95
  /**
96
96
  * Refreshes the user's profile information from the specified platform.
97
97
  * @param platform The target platform.
98
98
  * @param userId The user ID on the platform
99
99
  * @returns A promise resolving with the updated account profile information.
100
100
  */
101
- refreshProfile(platform: Platform, userId: string): Promise<ConnectedAccount>;
101
+ refreshProfile(platform: Platform, userId: string): Promise<ApiResponse<ConnectedAccount>>;
102
102
  /**
103
103
  * Gets the authentication status for the specified platform.
104
104
  * @param platform The target platform.
105
105
  * @returns A promise resolving with the authentication status response.
106
106
  */
107
- getAuthStatus(platform: Platform, userId: string): Promise<AuthStatusResponse>;
107
+ getAuthStatus(platform: Platform, userId: string): Promise<ApiResponse<AuthStatusResponse>>;
108
108
  /**
109
109
  * Unauthorizes a NEAR account from using the service
110
110
  * @returns A promise resolving with the unauthorized response
111
111
  */
112
- unauthorizeNear(): Promise<NearUnauthorizationResponse>;
112
+ unauthorizeNear(): Promise<ApiResponse<NearUnauthorizationResponse>>;
113
113
  /**
114
114
  * Revokes the authentication token for the specified platform.
115
115
  * @param platform The target platform.
116
116
  * @returns A promise resolving with the revocation response.
117
117
  */
118
- revokeAuth(platform: Platform, userId: string): Promise<AuthRevokeResponse>;
118
+ revokeAuth(platform: Platform, userId: string): Promise<ApiResponse<AuthRevokeResponse>>;
119
119
  /**
120
120
  * Lists all accounts connected to the NEAR account.
121
121
  * @returns A promise resolving with the connected accounts response containing an array of accounts.
122
122
  * @throws {CrosspostError} If the request fails or returns invalid data.
123
123
  */
124
- getConnectedAccounts(): Promise<ConnectedAccountsResponse>;
124
+ getConnectedAccounts(): Promise<ApiResponse<ConnectedAccountsResponse>>;
125
125
  }
126
126
 
127
127
  /**
@@ -139,43 +139,43 @@ declare class PostApi {
139
139
  * @param request The post creation request details.
140
140
  * @returns A promise resolving with the post creation response.
141
141
  */
142
- createPost(request: CreatePostRequest): Promise<CreatePostResponse>;
142
+ createPost(request: CreatePostRequest): Promise<ApiResponse<CreatePostResponse>>;
143
143
  /**
144
144
  * Reposts an existing post on the specified target platforms.
145
145
  * @param request The repost request details.
146
146
  * @returns A promise resolving with the repost response.
147
147
  */
148
- repost(request: RepostRequest): Promise<RepostResponse>;
148
+ repost(request: RepostRequest): Promise<ApiResponse<RepostResponse>>;
149
149
  /**
150
150
  * Quotes an existing post on the specified target platforms.
151
151
  * @param request The quote post request details.
152
152
  * @returns A promise resolving with the quote post response.
153
153
  */
154
- quotePost(request: QuotePostRequest): Promise<QuotePostResponse>;
154
+ quotePost(request: QuotePostRequest): Promise<ApiResponse<QuotePostResponse>>;
155
155
  /**
156
156
  * Replies to an existing post on the specified target platforms.
157
157
  * @param request The reply request details.
158
158
  * @returns A promise resolving with the reply response.
159
159
  */
160
- replyToPost(request: ReplyToPostRequest): Promise<ReplyToPostResponse>;
160
+ replyToPost(request: ReplyToPostRequest): Promise<ApiResponse<ReplyToPostResponse>>;
161
161
  /**
162
162
  * Likes a post on the specified target platforms.
163
163
  * @param request The like request details.
164
164
  * @returns A promise resolving with the like response.
165
165
  */
166
- likePost(request: LikePostRequest): Promise<LikePostResponse>;
166
+ likePost(request: LikePostRequest): Promise<ApiResponse<LikePostResponse>>;
167
167
  /**
168
168
  * Unlikes a post on the specified target platforms.
169
169
  * @param request The unlike request details.
170
170
  * @returns A promise resolving with the unlike response.
171
171
  */
172
- unlikePost(request: UnlikePostRequest): Promise<UnlikePostResponse>;
172
+ unlikePost(request: UnlikePostRequest): Promise<ApiResponse<UnlikePostResponse>>;
173
173
  /**
174
174
  * Deletes one or more posts.
175
175
  * @param request The delete request details.
176
176
  * @returns A promise resolving with the delete response.
177
177
  */
178
- deletePost(request: DeletePostRequest): Promise<DeletePostResponse>;
178
+ deletePost(request: DeletePostRequest): Promise<ApiResponse<DeletePostResponse>>;
179
179
  }
180
180
 
181
181
  /**
@@ -193,18 +193,18 @@ declare class SystemApi {
193
193
  * Gets the current rate limit status
194
194
  * @returns A promise resolving with the rate limit response
195
195
  */
196
- getRateLimits(): Promise<RateLimitResponse>;
196
+ getRateLimits(): Promise<ApiResponse<RateLimitResponse>>;
197
197
  /**
198
198
  * Gets the rate limit status for a specific endpoint
199
199
  * @param endpoint The endpoint to get rate limit for
200
200
  * @returns A promise resolving with the endpoint rate limit response
201
201
  */
202
- getEndpointRateLimit(endpoint: string): Promise<EndpointRateLimitResponse>;
202
+ getEndpointRateLimit(endpoint: string): Promise<ApiResponse<EndpointRateLimitResponse>>;
203
203
  /**
204
204
  * Gets the health status of the API
205
205
  * @returns A promise resolving with the health status
206
206
  */
207
- getHealthStatus(): Promise<HealthStatus>;
207
+ getHealthStatus(): Promise<ApiResponse<HealthStatus>>;
208
208
  }
209
209
 
210
210
  /**
@@ -215,7 +215,7 @@ interface CrosspostClientConfig {
215
215
  * Base URL for the Crosspost API
216
216
  * @default 'https://open-crosspost-proxy.deno.dev'
217
217
  */
218
- baseUrl?: string;
218
+ baseUrl?: string | URL;
219
219
  /**
220
220
  * NEAR authentication data obtained from near-sign-verify
221
221
  */
package/dist/index.js CHANGED
@@ -219,18 +219,13 @@ function createNetworkError(error, url, timeout) {
219
219
 
220
220
  // src/core/request.ts
221
221
  async function makeRequest(method, path, options, data, query) {
222
- let url = `${options.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
222
+ const url = new URL(path, options.baseUrl);
223
223
  if (query && typeof query === "object" && Object.keys(query).length > 0) {
224
- const queryParams = new URLSearchParams();
225
224
  for (const [key, value] of Object.entries(query)) {
226
225
  if (value !== void 0 && value !== null) {
227
- queryParams.append(key, String(value));
226
+ url.searchParams.append(key, String(value));
228
227
  }
229
228
  }
230
- const queryString = queryParams.toString();
231
- if (queryString) {
232
- url += `?${queryString}`;
233
- }
234
229
  }
235
230
  const context = {
236
231
  method,
@@ -295,24 +290,16 @@ async function makeRequest(method, path, options, data, query) {
295
290
  if (!response.ok) {
296
291
  throw handleErrorResponse(responseData, response.status);
297
292
  }
298
- if (!responseData || typeof responseData !== "object" || !("success" in responseData)) {
293
+ if (!responseData || typeof responseData !== "object" || !("success" in responseData) || !("meta" in responseData)) {
299
294
  throw new CrosspostError(
300
- "Invalid success response format from API",
295
+ "Invalid response format from API",
301
296
  ApiErrorCode2.INVALID_RESPONSE,
302
297
  response.status,
303
298
  { responseData }
304
299
  );
305
300
  }
306
301
  if (responseData.success) {
307
- if (!responseData.data) {
308
- throw new CrosspostError(
309
- "API returned success but no data",
310
- ApiErrorCode2.INVALID_RESPONSE,
311
- response.status,
312
- { responseData }
313
- );
314
- }
315
- return responseData.data;
302
+ return responseData;
316
303
  }
317
304
  throw handleErrorResponse(responseData, response.status);
318
305
  } catch (error) {
@@ -321,7 +308,10 @@ async function makeRequest(method, path, options, data, query) {
321
308
  throw enrichErrorWithContext(error, context);
322
309
  }
323
310
  if (error instanceof TypeError || error instanceof DOMException && error.name === "AbortError") {
324
- throw enrichErrorWithContext(createNetworkError(error, url, options.timeout), context);
311
+ throw enrichErrorWithContext(
312
+ createNetworkError(error, url.toString(), options.timeout),
313
+ context
314
+ );
325
315
  }
326
316
  throw enrichErrorWithContext(
327
317
  new CrosspostError(
@@ -426,15 +416,6 @@ function openAuthPopup(url, options = {}) {
426
416
  } else {
427
417
  reject(message.data);
428
418
  }
429
- setTimeout(() => {
430
- try {
431
- if (popup && !popup.closed) {
432
- popup.close();
433
- }
434
- } catch (e) {
435
- console.warn("Failed to close popup window:", e);
436
- }
437
- }, 100);
438
419
  }
439
420
  };
440
421
  window.addEventListener("message", handleMessage);
@@ -506,13 +487,20 @@ var AuthApi = class {
506
487
  * @throws Error if popups are blocked or if running in a non-browser environment.
507
488
  */
508
489
  async loginToPlatform(platform, options) {
509
- const { url } = await makeRequest(
490
+ const requestOptions = options || { redirect: false };
491
+ const response = await makeRequest(
510
492
  "POST",
511
493
  `/auth/${platform}/login`,
512
494
  this.options,
513
- options || { redirect: false }
495
+ requestOptions
514
496
  );
515
- const result = await openAuthPopup(url);
497
+ if (requestOptions.redirect) {
498
+ return response;
499
+ }
500
+ if (!response.data || !("url" in response.data)) {
501
+ throw new Error("Invalid authentication URL response");
502
+ }
503
+ const result = await openAuthPopup(response.data.url);
516
504
  if (!result.success || !result.userId) {
517
505
  throw new Error(result.error || "Authentication failed");
518
506
  }
@@ -753,7 +741,7 @@ var SystemApi = class {
753
741
 
754
742
  // src/core/config.ts
755
743
  var DEFAULT_CONFIG = {
756
- baseUrl: "https://open-crosspost-proxy.deno.dev/",
744
+ baseUrl: new URL("https://open-crosspost-proxy.deno.dev/"),
757
745
  timeout: 3e4
758
746
  };
759
747
 
@@ -768,7 +756,7 @@ var CrosspostClient = class {
768
756
  const timeout = config.timeout || DEFAULT_CONFIG.timeout;
769
757
  const nearAuthData = config.nearAuthData;
770
758
  this.options = {
771
- baseUrl,
759
+ baseUrl: baseUrl instanceof URL ? baseUrl : new URL(baseUrl),
772
760
  timeout,
773
761
  nearAuthData
774
762
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crosspost/sdk",
3
- "version": "0.1.14",
3
+ "version": "0.2.1",
4
4
  "description": "SDK for interacting with the Crosspost API",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -5,6 +5,7 @@ import type {
5
5
  AccountPostsResponse,
6
6
  ActivityLeaderboardQuery,
7
7
  ActivityLeaderboardResponse,
8
+ ApiResponse,
8
9
  } from '@crosspost/types';
9
10
  import { makeRequest, type RequestOptions } from '../core/request.ts';
10
11
 
@@ -27,7 +28,9 @@ export class ActivityApi {
27
28
  * @param query Optional query parameters
28
29
  * @returns A promise resolving with the leaderboard response
29
30
  */
30
- async getLeaderboard(query?: ActivityLeaderboardQuery): Promise<ActivityLeaderboardResponse> {
31
+ async getLeaderboard(
32
+ query?: ActivityLeaderboardQuery,
33
+ ): Promise<ApiResponse<ActivityLeaderboardResponse>> {
31
34
  return makeRequest<ActivityLeaderboardResponse, never, ActivityLeaderboardQuery>(
32
35
  'GET',
33
36
  '/api/activity',
@@ -46,7 +49,7 @@ export class ActivityApi {
46
49
  async getAccountActivity(
47
50
  signerId: string,
48
51
  query?: AccountActivityQuery,
49
- ): Promise<AccountActivityResponse> {
52
+ ): Promise<ApiResponse<AccountActivityResponse>> {
50
53
  return makeRequest<AccountActivityResponse, never, AccountActivityQuery>(
51
54
  'GET',
52
55
  `/api/activity/${signerId}`,
@@ -65,7 +68,7 @@ export class ActivityApi {
65
68
  async getAccountPosts(
66
69
  signerId: string,
67
70
  query?: AccountPostsQuery,
68
- ): Promise<AccountPostsResponse> {
71
+ ): Promise<ApiResponse<AccountPostsResponse>> {
69
72
  return makeRequest<AccountPostsResponse, never, AccountPostsQuery>(
70
73
  'GET',
71
74
  `/api/activity/${signerId}/posts`,
package/src/api/auth.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ ApiResponse,
2
3
  AuthCallbackResponse,
3
4
  AuthInitRequest,
4
5
  AuthRevokeResponse,
@@ -34,7 +35,7 @@ export class AuthApi {
34
35
  * Authorizes the NEAR account associated with the provided nearAuthData with the Crosspost service.
35
36
  * @returns A promise resolving with the authorization response.
36
37
  */
37
- async authorizeNearAccount(): Promise<NearAuthorizationResponse> {
38
+ async authorizeNearAccount(): Promise<ApiResponse<NearAuthorizationResponse>> {
38
39
  return makeRequest<NearAuthorizationResponse, NearAuthorizationRequest>(
39
40
  'POST',
40
41
  '/auth/authorize/near',
@@ -47,7 +48,7 @@ export class AuthApi {
47
48
  * Checks the authorization status of the NEAR account with the Crosspost service.
48
49
  * @returns A promise resolving with the authorization status response.
49
50
  */
50
- async getNearAuthorizationStatus(): Promise<NearAuthorizationResponse> {
51
+ async getNearAuthorizationStatus(): Promise<ApiResponse<NearAuthorizationResponse>> {
51
52
  return makeRequest<NearAuthorizationResponse, never>(
52
53
  'GET',
53
54
  '/auth/authorize/near/status',
@@ -65,17 +66,30 @@ export class AuthApi {
65
66
  async loginToPlatform(
66
67
  platform: Platform,
67
68
  options?: AuthInitRequest,
68
- ): Promise<AuthCallbackResponse> {
69
+ ): Promise<AuthCallbackResponse | ApiResponse<AuthUrlResponse>> {
70
+ // Use provided options or default to redirect: false
71
+ const requestOptions = options || { redirect: false };
72
+
69
73
  // Make POST request to get auth URL
70
- const { url } = await makeRequest<AuthUrlResponse, AuthInitRequest>(
74
+ const response = await makeRequest<AuthUrlResponse, AuthInitRequest>(
71
75
  'POST',
72
76
  `/auth/${platform}/login`,
73
77
  this.options,
74
- options || { redirect: false },
78
+ requestOptions,
75
79
  );
76
80
 
77
- // Open the popup with the auth URL
78
- const result = await openAuthPopup(url);
81
+ // If redirect is true, return the auth URL response directly
82
+ if (requestOptions.redirect) {
83
+ return response; // Return the full ApiResponse<AuthUrlResponse>
84
+ }
85
+
86
+ // Check if response.data exists and has the url property
87
+ if (!response.data || !('url' in response.data)) {
88
+ throw new Error('Invalid authentication URL response');
89
+ }
90
+
91
+ // Otherwise, continue with popup flow
92
+ const result = await openAuthPopup(response.data.url);
79
93
 
80
94
  if (!result.success || !result.userId) {
81
95
  throw new Error(result.error || 'Authentication failed');
@@ -94,7 +108,10 @@ export class AuthApi {
94
108
  * @param platform The target platform.
95
109
  * @returns A promise resolving with the refresh response containing updated auth details.
96
110
  */
97
- async refreshToken(platform: Platform, userId: string): Promise<AuthCallbackResponse> {
111
+ async refreshToken(
112
+ platform: Platform,
113
+ userId: string,
114
+ ): Promise<ApiResponse<AuthCallbackResponse>> {
98
115
  return makeRequest<AuthCallbackResponse, AuthTokenRequest>(
99
116
  'POST',
100
117
  `/auth/${platform}/refresh`,
@@ -109,7 +126,7 @@ export class AuthApi {
109
126
  * @param userId The user ID on the platform
110
127
  * @returns A promise resolving with the updated account profile information.
111
128
  */
112
- async refreshProfile(platform: Platform, userId: string): Promise<ConnectedAccount> {
129
+ async refreshProfile(platform: Platform, userId: string): Promise<ApiResponse<ConnectedAccount>> {
113
130
  return makeRequest<ConnectedAccount, AuthTokenRequest>(
114
131
  'POST',
115
132
  `/auth/${platform}/refresh-profile`,
@@ -123,7 +140,10 @@ export class AuthApi {
123
140
  * @param platform The target platform.
124
141
  * @returns A promise resolving with the authentication status response.
125
142
  */
126
- async getAuthStatus(platform: Platform, userId: string): Promise<AuthStatusResponse> {
143
+ async getAuthStatus(
144
+ platform: Platform,
145
+ userId: string,
146
+ ): Promise<ApiResponse<AuthStatusResponse>> {
127
147
  return makeRequest<AuthStatusResponse, never, AuthStatusParams>(
128
148
  'GET',
129
149
  `/auth/${platform}/status/${userId}`,
@@ -137,7 +157,7 @@ export class AuthApi {
137
157
  * Unauthorizes a NEAR account from using the service
138
158
  * @returns A promise resolving with the unauthorized response
139
159
  */
140
- async unauthorizeNear(): Promise<NearUnauthorizationResponse> {
160
+ async unauthorizeNear(): Promise<ApiResponse<NearUnauthorizationResponse>> {
141
161
  return makeRequest<NearUnauthorizationResponse, NearAuthorizationRequest>(
142
162
  'DELETE',
143
163
  '/auth/unauthorize/near',
@@ -151,7 +171,7 @@ export class AuthApi {
151
171
  * @param platform The target platform.
152
172
  * @returns A promise resolving with the revocation response.
153
173
  */
154
- async revokeAuth(platform: Platform, userId: string): Promise<AuthRevokeResponse> {
174
+ async revokeAuth(platform: Platform, userId: string): Promise<ApiResponse<AuthRevokeResponse>> {
155
175
  return makeRequest<AuthRevokeResponse, AuthTokenRequest>(
156
176
  'DELETE',
157
177
  `/auth/${platform}/revoke`,
@@ -165,7 +185,7 @@ export class AuthApi {
165
185
  * @returns A promise resolving with the connected accounts response containing an array of accounts.
166
186
  * @throws {CrosspostError} If the request fails or returns invalid data.
167
187
  */
168
- async getConnectedAccounts(): Promise<ConnectedAccountsResponse> {
188
+ async getConnectedAccounts(): Promise<ApiResponse<ConnectedAccountsResponse>> {
169
189
  return makeRequest<ConnectedAccountsResponse, never>(
170
190
  'GET',
171
191
  '/auth/accounts',
package/src/api/post.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ ApiResponse,
2
3
  CreatePostRequest,
3
4
  CreatePostResponse,
4
5
  DeletePostRequest,
@@ -35,7 +36,7 @@ export class PostApi {
35
36
  * @param request The post creation request details.
36
37
  * @returns A promise resolving with the post creation response.
37
38
  */
38
- async createPost(request: CreatePostRequest): Promise<CreatePostResponse> {
39
+ async createPost(request: CreatePostRequest): Promise<ApiResponse<CreatePostResponse>> {
39
40
  return makeRequest<CreatePostResponse, CreatePostRequest>(
40
41
  'POST',
41
42
  '/api/post',
@@ -49,7 +50,7 @@ export class PostApi {
49
50
  * @param request The repost request details.
50
51
  * @returns A promise resolving with the repost response.
51
52
  */
52
- async repost(request: RepostRequest): Promise<RepostResponse> {
53
+ async repost(request: RepostRequest): Promise<ApiResponse<RepostResponse>> {
53
54
  return makeRequest<RepostResponse, RepostRequest>(
54
55
  'POST',
55
56
  '/api/post/repost',
@@ -63,7 +64,7 @@ export class PostApi {
63
64
  * @param request The quote post request details.
64
65
  * @returns A promise resolving with the quote post response.
65
66
  */
66
- async quotePost(request: QuotePostRequest): Promise<QuotePostResponse> {
67
+ async quotePost(request: QuotePostRequest): Promise<ApiResponse<QuotePostResponse>> {
67
68
  return makeRequest<QuotePostResponse, QuotePostRequest>(
68
69
  'POST',
69
70
  '/api/post/quote',
@@ -77,7 +78,7 @@ export class PostApi {
77
78
  * @param request The reply request details.
78
79
  * @returns A promise resolving with the reply response.
79
80
  */
80
- async replyToPost(request: ReplyToPostRequest): Promise<ReplyToPostResponse> {
81
+ async replyToPost(request: ReplyToPostRequest): Promise<ApiResponse<ReplyToPostResponse>> {
81
82
  return makeRequest<ReplyToPostResponse, ReplyToPostRequest>(
82
83
  'POST',
83
84
  '/api/post/reply',
@@ -91,7 +92,7 @@ export class PostApi {
91
92
  * @param request The like request details.
92
93
  * @returns A promise resolving with the like response.
93
94
  */
94
- async likePost(request: LikePostRequest): Promise<LikePostResponse> {
95
+ async likePost(request: LikePostRequest): Promise<ApiResponse<LikePostResponse>> {
95
96
  return makeRequest<LikePostResponse, LikePostRequest>(
96
97
  'POST',
97
98
  `/api/post/like`,
@@ -105,7 +106,7 @@ export class PostApi {
105
106
  * @param request The unlike request details.
106
107
  * @returns A promise resolving with the unlike response.
107
108
  */
108
- async unlikePost(request: UnlikePostRequest): Promise<UnlikePostResponse> {
109
+ async unlikePost(request: UnlikePostRequest): Promise<ApiResponse<UnlikePostResponse>> {
109
110
  return makeRequest<UnlikePostResponse, UnlikePostRequest>(
110
111
  'DELETE',
111
112
  `/api/post/like`,
@@ -119,7 +120,7 @@ export class PostApi {
119
120
  * @param request The delete request details.
120
121
  * @returns A promise resolving with the delete response.
121
122
  */
122
- async deletePost(request: DeletePostRequest): Promise<DeletePostResponse> {
123
+ async deletePost(request: DeletePostRequest): Promise<ApiResponse<DeletePostResponse>> {
123
124
  return makeRequest<DeletePostResponse, DeletePostRequest>(
124
125
  'DELETE',
125
126
  `/api/post`,
package/src/api/system.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ ApiResponse,
2
3
  EndpointRateLimitResponse,
3
4
  HealthStatus,
4
5
  RateLimitEndpointParam,
@@ -25,7 +26,7 @@ export class SystemApi {
25
26
  * Gets the current rate limit status
26
27
  * @returns A promise resolving with the rate limit response
27
28
  */
28
- async getRateLimits(): Promise<RateLimitResponse> {
29
+ async getRateLimits(): Promise<ApiResponse<RateLimitResponse>> {
29
30
  return makeRequest<RateLimitResponse, never>(
30
31
  'GET',
31
32
  '/api/rate-limit',
@@ -38,7 +39,7 @@ export class SystemApi {
38
39
  * @param endpoint The endpoint to get rate limit for
39
40
  * @returns A promise resolving with the endpoint rate limit response
40
41
  */
41
- async getEndpointRateLimit(endpoint: string): Promise<EndpointRateLimitResponse> {
42
+ async getEndpointRateLimit(endpoint: string): Promise<ApiResponse<EndpointRateLimitResponse>> {
42
43
  return makeRequest<EndpointRateLimitResponse, never, RateLimitEndpointParam>(
43
44
  'GET',
44
45
  `/api/rate-limit/${endpoint}`,
@@ -52,7 +53,7 @@ export class SystemApi {
52
53
  * Gets the health status of the API
53
54
  * @returns A promise resolving with the health status
54
55
  */
55
- async getHealthStatus(): Promise<HealthStatus> {
56
+ async getHealthStatus(): Promise<ApiResponse<HealthStatus>> {
56
57
  return makeRequest<HealthStatus, never>(
57
58
  'GET',
58
59
  '/health',
@@ -28,7 +28,7 @@ export class CrosspostClient {
28
28
  const nearAuthData = config.nearAuthData;
29
29
 
30
30
  this.options = {
31
- baseUrl,
31
+ baseUrl: baseUrl instanceof URL ? baseUrl : new URL(baseUrl),
32
32
  timeout,
33
33
  nearAuthData,
34
34
  };
@@ -8,7 +8,7 @@ export interface CrosspostClientConfig {
8
8
  * Base URL for the Crosspost API
9
9
  * @default 'https://open-crosspost-proxy.deno.dev'
10
10
  */
11
- baseUrl?: string;
11
+ baseUrl?: string | URL;
12
12
  /**
13
13
  * NEAR authentication data obtained from near-sign-verify
14
14
  */
@@ -24,6 +24,6 @@ export interface CrosspostClientConfig {
24
24
  * Default configuration values for the CrosspostClient
25
25
  */
26
26
  export const DEFAULT_CONFIG: Required<Omit<CrosspostClientConfig, 'nearAuthData'>> = {
27
- baseUrl: 'https://open-crosspost-proxy.deno.dev/',
27
+ baseUrl: new URL('https://open-crosspost-proxy.deno.dev/'),
28
28
  timeout: 30000,
29
29
  };
@@ -1,4 +1,4 @@
1
- import { ApiErrorCode, type StatusCode } from '@crosspost/types';
1
+ import { ApiErrorCode, type ApiResponse, type StatusCode } from '@crosspost/types';
2
2
  import { createAuthToken, type NearAuthData } from 'near-sign-verify';
3
3
  import {
4
4
  createNetworkError,
@@ -14,7 +14,7 @@ export interface RequestOptions {
14
14
  /**
15
15
  * Base URL for the API
16
16
  */
17
- baseUrl: string;
17
+ baseUrl: URL;
18
18
  /**
19
19
  * NEAR authentication data for generating auth tokens
20
20
  * Required for non-GET requests, optional for GET requests
@@ -39,7 +39,7 @@ export interface RequestOptions {
39
39
  * @param options The request options
40
40
  * @param data Optional request data
41
41
  * @param query Optional query parameters
42
- * @returns A promise resolving with the data field from the API response
42
+ * @returns A promise resolving with the API response object
43
43
  * @throws {CrosspostError}
44
44
  * - If the request fails (network error, timeout)
45
45
  * - If the response is not valid JSON
@@ -57,21 +57,16 @@ export async function makeRequest<
57
57
  options: RequestOptions,
58
58
  data?: TRequest,
59
59
  query?: TQuery,
60
- ): Promise<TResponse> {
61
- let url = `${options.baseUrl}${path.startsWith('/') ? path : `/${path}`}`;
60
+ ): Promise<ApiResponse<TResponse>> {
61
+ const url = new URL(path, options.baseUrl);
62
62
 
63
63
  // Add query parameters if provided
64
64
  if (query && typeof query === 'object' && Object.keys(query).length > 0) {
65
- const queryParams = new URLSearchParams();
66
65
  for (const [key, value] of Object.entries(query)) {
67
66
  if (value !== undefined && value !== null) {
68
- queryParams.append(key, String(value));
67
+ url.searchParams.append(key, String(value));
69
68
  }
70
69
  }
71
- const queryString = queryParams.toString();
72
- if (queryString) {
73
- url += `?${queryString}`;
74
- }
75
70
  }
76
71
 
77
72
  // Create a context object for error enrichment
@@ -123,7 +118,7 @@ export async function makeRequest<
123
118
  const response = await fetch(url, requestOptions);
124
119
  clearTimeout(timeoutId);
125
120
 
126
- let responseData: any;
121
+ let responseData: ApiResponse<TResponse>;
127
122
  try {
128
123
  responseData = await response.json();
129
124
  } catch (jsonError) {
@@ -151,9 +146,12 @@ export async function makeRequest<
151
146
  }
152
147
 
153
148
  // Validate success response structure
154
- if (!responseData || typeof responseData !== 'object' || !('success' in responseData)) {
149
+ if (
150
+ !responseData || typeof responseData !== 'object' || !('success' in responseData) ||
151
+ !('meta' in responseData)
152
+ ) {
155
153
  throw new CrosspostError(
156
- 'Invalid success response format from API',
154
+ 'Invalid response format from API',
157
155
  ApiErrorCode.INVALID_RESPONSE,
158
156
  response.status as StatusCode,
159
157
  { responseData },
@@ -161,15 +159,7 @@ export async function makeRequest<
161
159
  }
162
160
 
163
161
  if (responseData.success) {
164
- if (!responseData.data) {
165
- throw new CrosspostError(
166
- 'API returned success but no data',
167
- ApiErrorCode.INVALID_RESPONSE,
168
- response.status as StatusCode,
169
- { responseData },
170
- );
171
- }
172
- return responseData.data as TResponse;
162
+ return responseData as ApiResponse<TResponse>;
173
163
  }
174
164
 
175
165
  // If we get here, we have response.ok but success: false
@@ -187,7 +177,10 @@ export async function makeRequest<
187
177
  if (
188
178
  error instanceof TypeError || (error instanceof DOMException && error.name === 'AbortError')
189
179
  ) {
190
- throw enrichErrorWithContext(createNetworkError(error, url, options.timeout), context);
180
+ throw enrichErrorWithContext(
181
+ createNetworkError(error, url.toString(), options.timeout),
182
+ context,
183
+ );
191
184
  }
192
185
 
193
186
  // For any other errors, wrap them with context
@@ -1,5 +1,5 @@
1
1
  // @ts-nocheck
2
- import type { AuthStatus, PlatformName } from '@crosspost/types';
2
+ import type { PlatformName } from '@crosspost/types';
3
3
  declare global {
4
4
  interface WindowEventMap {
5
5
  message: MessageEvent<AuthCallbackMessage>;
@@ -19,7 +19,11 @@ interface AuthCallbackData {
19
19
  userId?: string;
20
20
  error?: string;
21
21
  error_description?: string;
22
- status: AuthStatus;
22
+ status: {
23
+ message: string; // User-friendly status message
24
+ code: string; // Status code for programmatic handling
25
+ details?: string; // Additional details if needed
26
+ };
23
27
  }
24
28
 
25
29
  interface AuthCallbackMessage {
@@ -81,17 +85,6 @@ export function openAuthPopup(url: string, options: PopupOptions = {}): Promise<
81
85
  } else {
82
86
  reject(message.data);
83
87
  }
84
-
85
- // Give a moment for any final operations before closing
86
- setTimeout(() => {
87
- try {
88
- if (popup && !popup.closed) {
89
- popup.close();
90
- }
91
- } catch (e) {
92
- console.warn('Failed to close popup window:', e);
93
- }
94
- }, 100);
95
88
  }
96
89
  };
97
90