@crosspost/sdk 0.1.9 → 0.1.10
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 +23 -13
- package/dist/index.cjs +131 -4822
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +107 -4725
- package/package.json +2 -1
- package/src/api/auth.ts +31 -12
- package/src/core/request.ts +17 -2
- package/src/utils/popup.ts +129 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@crosspost/sdk",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.10",
|
4
4
|
"description": "SDK for interacting with the Crosspost API",
|
5
5
|
"type": "module",
|
6
6
|
"main": "dist/index.cjs",
|
@@ -36,6 +36,7 @@
|
|
36
36
|
"author": "crosspost.near",
|
37
37
|
"license": "MIT",
|
38
38
|
"dependencies": {
|
39
|
+
"@crosspost/types": "^0.1.9",
|
39
40
|
"near-sign-verify": "^0.1.3"
|
40
41
|
},
|
41
42
|
"devDependencies": {
|
package/src/api/auth.ts
CHANGED
@@ -5,7 +5,6 @@ import type {
|
|
5
5
|
AuthStatusParams,
|
6
6
|
AuthStatusResponse,
|
7
7
|
AuthTokenRequest,
|
8
|
-
AuthUrlResponse,
|
9
8
|
ConnectedAccount,
|
10
9
|
ConnectedAccountsResponse,
|
11
10
|
NearAuthorizationRequest,
|
@@ -14,6 +13,7 @@ import type {
|
|
14
13
|
Platform,
|
15
14
|
} from '@crosspost/types';
|
16
15
|
import { makeRequest, type RequestOptions } from '../core/request.ts';
|
16
|
+
import { openAuthPopup } from '../utils/popup.ts';
|
17
17
|
|
18
18
|
/**
|
19
19
|
* Authentication-related API operations
|
@@ -55,22 +55,40 @@ export class AuthApi {
|
|
55
55
|
}
|
56
56
|
|
57
57
|
/**
|
58
|
-
* Initiates the login process for a specific platform.
|
59
|
-
* The service handles the OAuth flow; this method triggers it.
|
58
|
+
* Initiates the login process for a specific platform using a popup window.
|
60
59
|
* @param platform The target platform.
|
61
60
|
* @param options Optional success and error redirect URLs.
|
62
|
-
* @returns
|
61
|
+
* @returns Promise that resolves with the authentication result when the popup completes.
|
62
|
+
* @throws Error if popups are blocked or if running in a non-browser environment.
|
63
63
|
*/
|
64
64
|
async loginToPlatform(
|
65
65
|
platform: Platform,
|
66
66
|
options?: AuthInitRequest,
|
67
|
-
): Promise<
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
)
|
67
|
+
): Promise<AuthCallbackResponse> {
|
68
|
+
// Construct the login URL
|
69
|
+
const baseUrl = this.options.baseUrl || '';
|
70
|
+
const loginUrl = new URL(`/auth/${platform}/login`, baseUrl);
|
71
|
+
|
72
|
+
// Add successUrl and errorUrl if provided
|
73
|
+
if (options?.successUrl) {
|
74
|
+
loginUrl.searchParams.set('successUrl', options.successUrl);
|
75
|
+
}
|
76
|
+
if (options?.errorUrl) {
|
77
|
+
loginUrl.searchParams.set('errorUrl', options.errorUrl);
|
78
|
+
}
|
79
|
+
|
80
|
+
// Open the popup and wait for the result
|
81
|
+
const result = await openAuthPopup(loginUrl.toString());
|
82
|
+
|
83
|
+
if (!result.success || !result.userId) {
|
84
|
+
throw new Error(result.error || 'Authentication failed');
|
85
|
+
}
|
86
|
+
|
87
|
+
// Return the result in the expected format
|
88
|
+
return {
|
89
|
+
platform,
|
90
|
+
userId: result.userId,
|
91
|
+
};
|
74
92
|
}
|
75
93
|
|
76
94
|
/**
|
@@ -146,7 +164,8 @@ export class AuthApi {
|
|
146
164
|
|
147
165
|
/**
|
148
166
|
* Lists all accounts connected to the NEAR account.
|
149
|
-
* @returns A promise resolving with the
|
167
|
+
* @returns A promise resolving with the connected accounts response containing an array of accounts.
|
168
|
+
* @throws {CrosspostError} If the request fails or returns invalid data.
|
150
169
|
*/
|
151
170
|
async getConnectedAccounts(): Promise<ConnectedAccountsResponse> {
|
152
171
|
return makeRequest<ConnectedAccountsResponse, never>(
|
package/src/core/request.ts
CHANGED
@@ -32,13 +32,20 @@ export interface RequestOptions {
|
|
32
32
|
}
|
33
33
|
|
34
34
|
/**
|
35
|
-
* Makes a request to the API with error handling
|
35
|
+
* Makes a request to the API with error handling and data extraction
|
36
36
|
*
|
37
37
|
* @param method The HTTP method
|
38
38
|
* @param path The API path
|
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
|
43
|
+
* @throws {CrosspostError}
|
44
|
+
* - If the request fails (network error, timeout)
|
45
|
+
* - If the response is not valid JSON
|
46
|
+
* - If the response does not follow the expected ApiResponse format
|
47
|
+
* - If the response indicates success but contains no data
|
48
|
+
* - If the response indicates failure (includes error details and metadata)
|
42
49
|
*/
|
43
50
|
export async function makeRequest<
|
44
51
|
TResponse,
|
@@ -154,6 +161,14 @@ export async function makeRequest<
|
|
154
161
|
}
|
155
162
|
|
156
163
|
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
|
+
}
|
157
172
|
return responseData.data as TResponse;
|
158
173
|
}
|
159
174
|
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import type { PlatformName } from '@crosspost/types';
|
2
|
+
|
3
|
+
// Augment the Window interface
|
4
|
+
declare global {
|
5
|
+
interface Window {
|
6
|
+
innerWidth: number;
|
7
|
+
innerHeight: number;
|
8
|
+
open(url: string, target: string, features: string): Window | null;
|
9
|
+
}
|
10
|
+
|
11
|
+
interface WindowEventMap {
|
12
|
+
message: MessageEvent<AuthCallbackMessage>;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
interface PopupOptions {
|
17
|
+
width?: number;
|
18
|
+
height?: number;
|
19
|
+
left?: number;
|
20
|
+
top?: number;
|
21
|
+
}
|
22
|
+
|
23
|
+
interface AuthCallbackData {
|
24
|
+
success: boolean;
|
25
|
+
platform: PlatformName;
|
26
|
+
userId?: string;
|
27
|
+
error?: string;
|
28
|
+
error_description?: string;
|
29
|
+
}
|
30
|
+
|
31
|
+
interface AuthCallbackMessage {
|
32
|
+
type: 'AUTH_CALLBACK';
|
33
|
+
data: AuthCallbackData;
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Opens a popup window and returns a promise that resolves when the authentication is complete
|
38
|
+
* @param url The URL to open in the popup
|
39
|
+
* @param options Optional popup window dimensions and position
|
40
|
+
* @returns Promise that resolves with the authentication result
|
41
|
+
* @throws Error if popups are blocked or if running in a non-browser environment
|
42
|
+
*/
|
43
|
+
export function openAuthPopup(url: string, options: PopupOptions = {}): Promise<AuthCallbackData> {
|
44
|
+
// Check for browser environment
|
45
|
+
if (typeof window === 'undefined') {
|
46
|
+
throw new Error('openAuthPopup can only be used in a browser environment');
|
47
|
+
}
|
48
|
+
|
49
|
+
return new Promise((resolve, reject) => {
|
50
|
+
// Calculate popup dimensions and position
|
51
|
+
const {
|
52
|
+
width = 600,
|
53
|
+
height = 700,
|
54
|
+
left = Math.max(0, (window.innerWidth - 600) / 2),
|
55
|
+
top = Math.max(0, (window.innerHeight - 700) / 2),
|
56
|
+
} = options;
|
57
|
+
|
58
|
+
// Open the popup
|
59
|
+
const popup = window.open(
|
60
|
+
url,
|
61
|
+
'authPopup',
|
62
|
+
`width=${width},height=${height},left=${left},top=${top},scrollbars=yes`,
|
63
|
+
);
|
64
|
+
|
65
|
+
if (!popup) {
|
66
|
+
reject(new Error('Popup blocked. Please allow popups for this site.'));
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
|
70
|
+
let messageReceived = false;
|
71
|
+
|
72
|
+
// Function to handle messages from the popup with proper typing
|
73
|
+
const handleMessage = (event: MessageEvent<AuthCallbackMessage>) => {
|
74
|
+
// Verify the message is from our popup and popup exists
|
75
|
+
if (!popup || event.source !== popup) {
|
76
|
+
return;
|
77
|
+
}
|
78
|
+
|
79
|
+
const message = event.data;
|
80
|
+
if (message?.type === 'AUTH_CALLBACK') {
|
81
|
+
messageReceived = true;
|
82
|
+
window.removeEventListener('message', handleMessage);
|
83
|
+
clearInterval(checkClosedInterval);
|
84
|
+
|
85
|
+
if (message.data.success) {
|
86
|
+
resolve(message.data);
|
87
|
+
} else {
|
88
|
+
reject(message.data);
|
89
|
+
}
|
90
|
+
|
91
|
+
// Give a moment for any final operations before closing
|
92
|
+
setTimeout(() => {
|
93
|
+
try {
|
94
|
+
if (popup && !popup.closed) {
|
95
|
+
popup.close();
|
96
|
+
}
|
97
|
+
} catch (e) {
|
98
|
+
console.warn('Failed to close popup window:', e);
|
99
|
+
}
|
100
|
+
}, 100);
|
101
|
+
}
|
102
|
+
};
|
103
|
+
|
104
|
+
// Listen for messages from the popup
|
105
|
+
window.addEventListener('message', handleMessage as EventListener);
|
106
|
+
|
107
|
+
// Check if popup was closed manually
|
108
|
+
const checkClosedInterval = setInterval(() => {
|
109
|
+
try {
|
110
|
+
if (!popup || popup.closed) {
|
111
|
+
cleanup();
|
112
|
+
}
|
113
|
+
} catch (e) {
|
114
|
+
console.warn('Error checking popup state:', e);
|
115
|
+
cleanup();
|
116
|
+
}
|
117
|
+
}, 500);
|
118
|
+
|
119
|
+
// Cleanup function to handle popup closure
|
120
|
+
function cleanup() {
|
121
|
+
clearInterval(checkClosedInterval);
|
122
|
+
window.removeEventListener('message', handleMessage as EventListener);
|
123
|
+
|
124
|
+
if (!messageReceived) {
|
125
|
+
reject(new Error('Authentication cancelled by user.'));
|
126
|
+
}
|
127
|
+
}
|
128
|
+
});
|
129
|
+
}
|