@crosspost/sdk 0.1.3 → 0.1.4
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/LICENSE +21 -0
- package/README.md +85 -93
- package/dist/index.cjs +73 -160
- package/dist/index.d.cts +18 -43
- package/dist/index.d.ts +18 -43
- package/dist/index.js +69 -141
- package/package.json +1 -3
- package/src/core/client.ts +3 -5
- package/src/core/request.ts +1 -11
- package/src/index.ts +3 -12
- package/src/utils/error.ts +300 -0
- package/src/utils/cookie.ts +0 -91
- package/src/utils/error-utils.ts +0 -310
package/src/utils/error.ts
CHANGED
@@ -1,5 +1,305 @@
|
|
1
1
|
import { ApiError, ApiErrorCode, Platform, PlatformError } from '@crosspost/types';
|
2
2
|
|
3
|
+
/**
|
4
|
+
* Error categories grouped by type
|
5
|
+
*/
|
6
|
+
export const ERROR_CATEGORIES = {
|
7
|
+
AUTH: [
|
8
|
+
ApiErrorCode.UNAUTHORIZED,
|
9
|
+
ApiErrorCode.FORBIDDEN,
|
10
|
+
],
|
11
|
+
VALIDATION: [
|
12
|
+
ApiErrorCode.VALIDATION_ERROR,
|
13
|
+
ApiErrorCode.INVALID_REQUEST,
|
14
|
+
],
|
15
|
+
NETWORK: [
|
16
|
+
ApiErrorCode.NETWORK_ERROR,
|
17
|
+
],
|
18
|
+
PLATFORM: [
|
19
|
+
ApiErrorCode.PLATFORM_ERROR,
|
20
|
+
ApiErrorCode.PLATFORM_UNAVAILABLE,
|
21
|
+
],
|
22
|
+
CONTENT: [
|
23
|
+
ApiErrorCode.CONTENT_POLICY_VIOLATION,
|
24
|
+
ApiErrorCode.DUPLICATE_CONTENT,
|
25
|
+
],
|
26
|
+
RATE_LIMIT: [
|
27
|
+
ApiErrorCode.RATE_LIMITED,
|
28
|
+
],
|
29
|
+
POST: [
|
30
|
+
ApiErrorCode.POST_CREATION_FAILED,
|
31
|
+
ApiErrorCode.THREAD_CREATION_FAILED,
|
32
|
+
ApiErrorCode.POST_DELETION_FAILED,
|
33
|
+
ApiErrorCode.POST_INTERACTION_FAILED,
|
34
|
+
],
|
35
|
+
MEDIA: [
|
36
|
+
ApiErrorCode.MEDIA_UPLOAD_FAILED,
|
37
|
+
],
|
38
|
+
};
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Check if an error belongs to a specific category
|
42
|
+
*
|
43
|
+
* @param error The error to check
|
44
|
+
* @param category The category to check against
|
45
|
+
* @returns True if the error belongs to the category, false otherwise
|
46
|
+
*/
|
47
|
+
export function isErrorOfCategory(error: unknown, category: ApiErrorCode[]): boolean {
|
48
|
+
if (error instanceof ApiError) {
|
49
|
+
return category.includes(error.code);
|
50
|
+
}
|
51
|
+
|
52
|
+
if (error instanceof PlatformError) {
|
53
|
+
return category.includes(error.code);
|
54
|
+
}
|
55
|
+
|
56
|
+
// Fallback for error-like objects with code property
|
57
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
58
|
+
return category.includes((error as any).code);
|
59
|
+
}
|
60
|
+
|
61
|
+
return false;
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Check if an error is an authentication error
|
66
|
+
*
|
67
|
+
* @param error The error to check
|
68
|
+
* @returns True if the error is an authentication error, false otherwise
|
69
|
+
*/
|
70
|
+
export function isAuthError(error: unknown): boolean {
|
71
|
+
return isErrorOfCategory(error, ERROR_CATEGORIES.AUTH);
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Check if an error is a validation error
|
76
|
+
*
|
77
|
+
* @param error The error to check
|
78
|
+
* @returns True if the error is a validation error, false otherwise
|
79
|
+
*/
|
80
|
+
export function isValidationError(error: unknown): boolean {
|
81
|
+
return isErrorOfCategory(error, ERROR_CATEGORIES.VALIDATION);
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Check if an error is a network error
|
86
|
+
*
|
87
|
+
* @param error The error to check
|
88
|
+
* @returns True if the error is a network error, false otherwise
|
89
|
+
*/
|
90
|
+
export function isNetworkError(error: unknown): boolean {
|
91
|
+
return isErrorOfCategory(error, ERROR_CATEGORIES.NETWORK);
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Check if an error is a platform error
|
96
|
+
*
|
97
|
+
* @param error The error to check
|
98
|
+
* @returns True if the error is a platform error, false otherwise
|
99
|
+
*/
|
100
|
+
export function isPlatformError(error: unknown): boolean {
|
101
|
+
return isErrorOfCategory(error, ERROR_CATEGORIES.PLATFORM) || error instanceof PlatformError;
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
* Check if an error is a content policy error
|
106
|
+
*
|
107
|
+
* @param error The error to check
|
108
|
+
* @returns True if the error is a content policy error, false otherwise
|
109
|
+
*/
|
110
|
+
export function isContentError(error: unknown): boolean {
|
111
|
+
return isErrorOfCategory(error, ERROR_CATEGORIES.CONTENT);
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Check if an error is a rate limit error
|
116
|
+
*
|
117
|
+
* @param error The error to check
|
118
|
+
* @returns True if the error is a rate limit error, false otherwise
|
119
|
+
*/
|
120
|
+
export function isRateLimitError(error: unknown): boolean {
|
121
|
+
return isErrorOfCategory(error, ERROR_CATEGORIES.RATE_LIMIT);
|
122
|
+
}
|
123
|
+
|
124
|
+
/**
|
125
|
+
* Check if an error is a post-related error
|
126
|
+
*
|
127
|
+
* @param error The error to check
|
128
|
+
* @returns True if the error is a post-related error, false otherwise
|
129
|
+
*/
|
130
|
+
export function isPostError(error: unknown): boolean {
|
131
|
+
return isErrorOfCategory(error, ERROR_CATEGORIES.POST);
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* Check if an error is a media-related error
|
136
|
+
*
|
137
|
+
* @param error The error to check
|
138
|
+
* @returns True if the error is a media-related error, false otherwise
|
139
|
+
*/
|
140
|
+
export function isMediaError(error: unknown): boolean {
|
141
|
+
return isErrorOfCategory(error, ERROR_CATEGORIES.MEDIA);
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* Check if an error is recoverable
|
146
|
+
*
|
147
|
+
* @param error The error to check
|
148
|
+
* @returns True if the error is recoverable, false otherwise
|
149
|
+
*/
|
150
|
+
export function isRecoverableError(error: unknown): boolean {
|
151
|
+
if (error instanceof ApiError || error instanceof PlatformError) {
|
152
|
+
return error.recoverable;
|
153
|
+
}
|
154
|
+
|
155
|
+
return false;
|
156
|
+
}
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Get a user-friendly error message
|
160
|
+
*
|
161
|
+
* @param error The error to get the message from
|
162
|
+
* @param defaultMessage The default message to return if no message is found
|
163
|
+
* @returns The error message
|
164
|
+
*/
|
165
|
+
export function getErrorMessage(
|
166
|
+
error: unknown,
|
167
|
+
defaultMessage: string = 'An error occurred',
|
168
|
+
): string {
|
169
|
+
if (error instanceof Error) {
|
170
|
+
return error.message || defaultMessage;
|
171
|
+
}
|
172
|
+
|
173
|
+
if (typeof error === 'string') {
|
174
|
+
return error;
|
175
|
+
}
|
176
|
+
|
177
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
178
|
+
return (error as any).message || defaultMessage;
|
179
|
+
}
|
180
|
+
|
181
|
+
return defaultMessage;
|
182
|
+
}
|
183
|
+
|
184
|
+
/**
|
185
|
+
* Extract error details if available
|
186
|
+
*
|
187
|
+
* @param error The error to extract details from
|
188
|
+
* @returns The error details or undefined if none are found
|
189
|
+
*/
|
190
|
+
export function getErrorDetails(error: unknown): Record<string, any> | undefined {
|
191
|
+
if (error instanceof ApiError || error instanceof PlatformError) {
|
192
|
+
return error.details;
|
193
|
+
}
|
194
|
+
|
195
|
+
if (error && typeof error === 'object' && 'details' in error) {
|
196
|
+
return (error as any).details;
|
197
|
+
}
|
198
|
+
|
199
|
+
return undefined;
|
200
|
+
}
|
201
|
+
|
202
|
+
/**
|
203
|
+
* Enrich an error with additional context
|
204
|
+
*
|
205
|
+
* @param error The error to enrich
|
206
|
+
* @param context The context to add to the error
|
207
|
+
* @returns The enriched error
|
208
|
+
*/
|
209
|
+
export function enrichErrorWithContext(
|
210
|
+
error: unknown,
|
211
|
+
context: Record<string, any>,
|
212
|
+
): Error {
|
213
|
+
if (error instanceof ApiError) {
|
214
|
+
// Create a new ApiError with the merged details since details is read-only
|
215
|
+
return new ApiError(
|
216
|
+
error.message,
|
217
|
+
error.code,
|
218
|
+
error.status,
|
219
|
+
{ ...(error.details || {}), ...context },
|
220
|
+
error.recoverable,
|
221
|
+
);
|
222
|
+
}
|
223
|
+
|
224
|
+
if (error instanceof PlatformError) {
|
225
|
+
// Create a new PlatformError with the merged details since details is read-only
|
226
|
+
return new PlatformError(
|
227
|
+
error.message,
|
228
|
+
error.platform,
|
229
|
+
error.code,
|
230
|
+
error.recoverable,
|
231
|
+
error.originalError,
|
232
|
+
error.status,
|
233
|
+
error.userId,
|
234
|
+
{ ...(error.details || {}), ...context },
|
235
|
+
);
|
236
|
+
}
|
237
|
+
|
238
|
+
// For regular errors or non-Error objects, create a new ApiError with the context
|
239
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
240
|
+
return new ApiError(
|
241
|
+
errorMessage || 'An error occurred',
|
242
|
+
ApiErrorCode.INTERNAL_ERROR,
|
243
|
+
500,
|
244
|
+
{ originalError: error, ...context },
|
245
|
+
false,
|
246
|
+
);
|
247
|
+
}
|
248
|
+
|
249
|
+
/**
|
250
|
+
* Wrapper for API calls with consistent error handling
|
251
|
+
*
|
252
|
+
* @param apiCall The API call to wrap
|
253
|
+
* @param context Optional context to add to any errors
|
254
|
+
* @returns The result of the API call
|
255
|
+
* @throws An enriched error if the API call fails
|
256
|
+
*/
|
257
|
+
export async function apiWrapper<T>(
|
258
|
+
apiCall: () => Promise<T>,
|
259
|
+
context?: Record<string, any>,
|
260
|
+
): Promise<T> {
|
261
|
+
try {
|
262
|
+
return await apiCall();
|
263
|
+
} catch (error) {
|
264
|
+
// If it's a Response object, use handleErrorResponse
|
265
|
+
if (error instanceof Response) {
|
266
|
+
try {
|
267
|
+
const errorData = await error.json();
|
268
|
+
throw enrichErrorWithContext(
|
269
|
+
handleErrorResponse(errorData, error.status),
|
270
|
+
context || {},
|
271
|
+
);
|
272
|
+
} catch (jsonError) {
|
273
|
+
// If JSON parsing fails, create a generic error
|
274
|
+
if (jsonError instanceof Error && jsonError.name === 'SyntaxError') {
|
275
|
+
throw enrichErrorWithContext(
|
276
|
+
new ApiError(
|
277
|
+
`API request failed with status ${error.status} and non-JSON response`,
|
278
|
+
ApiErrorCode.NETWORK_ERROR,
|
279
|
+
error.status as any,
|
280
|
+
{ originalResponse: error.statusText },
|
281
|
+
),
|
282
|
+
context || {},
|
283
|
+
);
|
284
|
+
}
|
285
|
+
// If it's already an enriched error from handleErrorResponse, just throw it
|
286
|
+
throw jsonError;
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
290
|
+
// If it's already an ApiError or PlatformError, just add context
|
291
|
+
if (error instanceof ApiError || error instanceof PlatformError) {
|
292
|
+
throw enrichErrorWithContext(error, context || {});
|
293
|
+
}
|
294
|
+
|
295
|
+
// Otherwise wrap it in an ApiError
|
296
|
+
throw enrichErrorWithContext(
|
297
|
+
error instanceof Error ? error : new Error(String(error)),
|
298
|
+
context || {},
|
299
|
+
);
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
3
303
|
/**
|
4
304
|
* Handles error responses from the API and converts them to appropriate error objects.
|
5
305
|
*
|
package/src/utils/cookie.ts
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
import Cookies from 'js-cookie';
|
2
|
-
import type { NearAuthData } from 'near-sign-verify';
|
3
|
-
|
4
|
-
export const AUTH_COOKIE_NAME = '__crosspost_auth';
|
5
|
-
export const CSRF_COOKIE_NAME = 'XSRF-TOKEN';
|
6
|
-
export const CSRF_HEADER_NAME = 'X-CSRF-Token';
|
7
|
-
|
8
|
-
/**
|
9
|
-
* Checks if the code is running in a Deno environment
|
10
|
-
*/
|
11
|
-
export const isDeno = (): boolean => { // monorepo builds primarily for Deno
|
12
|
-
// we could expect that frontends in Deno environment will use this package,
|
13
|
-
// and then we can determine auth solution there (ValTown, Smallweb, etc)
|
14
|
-
return typeof (globalThis as any).Deno !== 'undefined';
|
15
|
-
};
|
16
|
-
|
17
|
-
/**
|
18
|
-
* Checks if the code is running in a browser environment
|
19
|
-
*/
|
20
|
-
export const isBrowser = (): boolean => {
|
21
|
-
return !isDeno() && typeof globalThis.window !== 'undefined';
|
22
|
-
};
|
23
|
-
|
24
|
-
export const AUTH_COOKIE_OPTIONS: Cookies.CookieAttributes = {
|
25
|
-
secure: true,
|
26
|
-
sameSite: 'lax', // Restrict to same-site and top-level navigation
|
27
|
-
path: '/',
|
28
|
-
expires: 30, // 30 days
|
29
|
-
};
|
30
|
-
|
31
|
-
/**
|
32
|
-
* Gets authentication data from the cookie
|
33
|
-
* @returns The NearAuthData object or undefined if not found
|
34
|
-
*/
|
35
|
-
export function getAuthFromCookie(): NearAuthData | undefined {
|
36
|
-
try {
|
37
|
-
if (!isBrowser()) {
|
38
|
-
return undefined;
|
39
|
-
}
|
40
|
-
|
41
|
-
const cookieValue = Cookies.get(AUTH_COOKIE_NAME);
|
42
|
-
if (!cookieValue) {
|
43
|
-
return undefined;
|
44
|
-
}
|
45
|
-
|
46
|
-
return JSON.parse(cookieValue) as NearAuthData;
|
47
|
-
} catch (error) {
|
48
|
-
console.error('Failed to parse auth cookie:', error);
|
49
|
-
return undefined;
|
50
|
-
}
|
51
|
-
}
|
52
|
-
|
53
|
-
/**
|
54
|
-
* Stores authentication data in a secure cookie
|
55
|
-
* @param authData The NearAuthData object to store
|
56
|
-
*/
|
57
|
-
export function storeAuthInCookie(authData: NearAuthData): void {
|
58
|
-
try {
|
59
|
-
if (!isBrowser()) {
|
60
|
-
return;
|
61
|
-
}
|
62
|
-
|
63
|
-
const cookieValue = JSON.stringify(authData);
|
64
|
-
Cookies.set(AUTH_COOKIE_NAME, cookieValue, AUTH_COOKIE_OPTIONS);
|
65
|
-
} catch (error) {
|
66
|
-
console.error('Failed to store auth cookie:', error);
|
67
|
-
}
|
68
|
-
}
|
69
|
-
|
70
|
-
/**
|
71
|
-
* Clears the authentication cookie
|
72
|
-
*/
|
73
|
-
export function clearAuthCookie(): void {
|
74
|
-
if (!isBrowser()) {
|
75
|
-
return;
|
76
|
-
}
|
77
|
-
|
78
|
-
Cookies.remove(AUTH_COOKIE_NAME, { path: AUTH_COOKIE_OPTIONS.path });
|
79
|
-
}
|
80
|
-
|
81
|
-
/**
|
82
|
-
* Gets the CSRF token from the cookie
|
83
|
-
* @returns The CSRF token or undefined if not found
|
84
|
-
*/
|
85
|
-
export function getCsrfToken(): string | undefined {
|
86
|
-
if (!isBrowser()) {
|
87
|
-
return undefined;
|
88
|
-
}
|
89
|
-
|
90
|
-
return Cookies.get(CSRF_COOKIE_NAME);
|
91
|
-
}
|
package/src/utils/error-utils.ts
DELETED
@@ -1,310 +0,0 @@
|
|
1
|
-
import { ApiError, ApiErrorCode, Platform, PlatformError } from '@crosspost/types';
|
2
|
-
import { handleErrorResponse as baseHandleErrorResponse } from './error.ts';
|
3
|
-
|
4
|
-
/**
|
5
|
-
* Error categories grouped by type for better organization and usage
|
6
|
-
*/
|
7
|
-
export const ERROR_CATEGORIES = {
|
8
|
-
AUTH: [
|
9
|
-
ApiErrorCode.UNAUTHORIZED,
|
10
|
-
ApiErrorCode.FORBIDDEN,
|
11
|
-
],
|
12
|
-
VALIDATION: [
|
13
|
-
ApiErrorCode.VALIDATION_ERROR,
|
14
|
-
ApiErrorCode.INVALID_REQUEST,
|
15
|
-
],
|
16
|
-
NETWORK: [
|
17
|
-
ApiErrorCode.NETWORK_ERROR,
|
18
|
-
],
|
19
|
-
PLATFORM: [
|
20
|
-
ApiErrorCode.PLATFORM_ERROR,
|
21
|
-
ApiErrorCode.PLATFORM_UNAVAILABLE,
|
22
|
-
],
|
23
|
-
CONTENT: [
|
24
|
-
ApiErrorCode.CONTENT_POLICY_VIOLATION,
|
25
|
-
ApiErrorCode.DUPLICATE_CONTENT,
|
26
|
-
],
|
27
|
-
RATE_LIMIT: [
|
28
|
-
ApiErrorCode.RATE_LIMITED,
|
29
|
-
],
|
30
|
-
POST: [
|
31
|
-
ApiErrorCode.POST_CREATION_FAILED,
|
32
|
-
ApiErrorCode.THREAD_CREATION_FAILED,
|
33
|
-
ApiErrorCode.POST_DELETION_FAILED,
|
34
|
-
ApiErrorCode.POST_INTERACTION_FAILED,
|
35
|
-
],
|
36
|
-
MEDIA: [
|
37
|
-
ApiErrorCode.MEDIA_UPLOAD_FAILED,
|
38
|
-
],
|
39
|
-
};
|
40
|
-
|
41
|
-
/**
|
42
|
-
* Check if an error belongs to a specific category
|
43
|
-
*
|
44
|
-
* @param error The error to check
|
45
|
-
* @param category The category to check against
|
46
|
-
* @returns True if the error belongs to the category, false otherwise
|
47
|
-
*/
|
48
|
-
export function isErrorOfCategory(error: unknown, category: ApiErrorCode[]): boolean {
|
49
|
-
if (error instanceof ApiError) {
|
50
|
-
return category.includes(error.code);
|
51
|
-
}
|
52
|
-
|
53
|
-
if (error instanceof PlatformError) {
|
54
|
-
return category.includes(error.code);
|
55
|
-
}
|
56
|
-
|
57
|
-
// Fallback for error-like objects with code property
|
58
|
-
if (error && typeof error === 'object' && 'code' in error) {
|
59
|
-
return category.includes((error as any).code);
|
60
|
-
}
|
61
|
-
|
62
|
-
return false;
|
63
|
-
}
|
64
|
-
|
65
|
-
/**
|
66
|
-
* Check if an error is an authentication error
|
67
|
-
*
|
68
|
-
* @param error The error to check
|
69
|
-
* @returns True if the error is an authentication error, false otherwise
|
70
|
-
*/
|
71
|
-
export function isAuthError(error: unknown): boolean {
|
72
|
-
return isErrorOfCategory(error, ERROR_CATEGORIES.AUTH);
|
73
|
-
}
|
74
|
-
|
75
|
-
/**
|
76
|
-
* Check if an error is a validation error
|
77
|
-
*
|
78
|
-
* @param error The error to check
|
79
|
-
* @returns True if the error is a validation error, false otherwise
|
80
|
-
*/
|
81
|
-
export function isValidationError(error: unknown): boolean {
|
82
|
-
return isErrorOfCategory(error, ERROR_CATEGORIES.VALIDATION);
|
83
|
-
}
|
84
|
-
|
85
|
-
/**
|
86
|
-
* Check if an error is a network error
|
87
|
-
*
|
88
|
-
* @param error The error to check
|
89
|
-
* @returns True if the error is a network error, false otherwise
|
90
|
-
*/
|
91
|
-
export function isNetworkError(error: unknown): boolean {
|
92
|
-
return isErrorOfCategory(error, ERROR_CATEGORIES.NETWORK);
|
93
|
-
}
|
94
|
-
|
95
|
-
/**
|
96
|
-
* Check if an error is a platform error
|
97
|
-
*
|
98
|
-
* @param error The error to check
|
99
|
-
* @returns True if the error is a platform error, false otherwise
|
100
|
-
*/
|
101
|
-
export function isPlatformError(error: unknown): boolean {
|
102
|
-
return isErrorOfCategory(error, ERROR_CATEGORIES.PLATFORM) || error instanceof PlatformError;
|
103
|
-
}
|
104
|
-
|
105
|
-
/**
|
106
|
-
* Check if an error is a content policy error
|
107
|
-
*
|
108
|
-
* @param error The error to check
|
109
|
-
* @returns True if the error is a content policy error, false otherwise
|
110
|
-
*/
|
111
|
-
export function isContentError(error: unknown): boolean {
|
112
|
-
return isErrorOfCategory(error, ERROR_CATEGORIES.CONTENT);
|
113
|
-
}
|
114
|
-
|
115
|
-
/**
|
116
|
-
* Check if an error is a rate limit error
|
117
|
-
*
|
118
|
-
* @param error The error to check
|
119
|
-
* @returns True if the error is a rate limit error, false otherwise
|
120
|
-
*/
|
121
|
-
export function isRateLimitError(error: unknown): boolean {
|
122
|
-
return isErrorOfCategory(error, ERROR_CATEGORIES.RATE_LIMIT);
|
123
|
-
}
|
124
|
-
|
125
|
-
/**
|
126
|
-
* Check if an error is a post-related error
|
127
|
-
*
|
128
|
-
* @param error The error to check
|
129
|
-
* @returns True if the error is a post-related error, false otherwise
|
130
|
-
*/
|
131
|
-
export function isPostError(error: unknown): boolean {
|
132
|
-
return isErrorOfCategory(error, ERROR_CATEGORIES.POST);
|
133
|
-
}
|
134
|
-
|
135
|
-
/**
|
136
|
-
* Check if an error is a media-related error
|
137
|
-
*
|
138
|
-
* @param error The error to check
|
139
|
-
* @returns True if the error is a media-related error, false otherwise
|
140
|
-
*/
|
141
|
-
export function isMediaError(error: unknown): boolean {
|
142
|
-
return isErrorOfCategory(error, ERROR_CATEGORIES.MEDIA);
|
143
|
-
}
|
144
|
-
|
145
|
-
/**
|
146
|
-
* Check if an error is recoverable
|
147
|
-
*
|
148
|
-
* @param error The error to check
|
149
|
-
* @returns True if the error is recoverable, false otherwise
|
150
|
-
*/
|
151
|
-
export function isRecoverableError(error: unknown): boolean {
|
152
|
-
if (error instanceof ApiError || error instanceof PlatformError) {
|
153
|
-
return error.recoverable;
|
154
|
-
}
|
155
|
-
|
156
|
-
return false;
|
157
|
-
}
|
158
|
-
|
159
|
-
/**
|
160
|
-
* Get a user-friendly error message
|
161
|
-
*
|
162
|
-
* @param error The error to get the message from
|
163
|
-
* @param defaultMessage The default message to return if no message is found
|
164
|
-
* @returns The error message
|
165
|
-
*/
|
166
|
-
export function getErrorMessage(
|
167
|
-
error: unknown,
|
168
|
-
defaultMessage: string = 'An error occurred',
|
169
|
-
): string {
|
170
|
-
if (error instanceof Error) {
|
171
|
-
return error.message || defaultMessage;
|
172
|
-
}
|
173
|
-
|
174
|
-
if (typeof error === 'string') {
|
175
|
-
return error;
|
176
|
-
}
|
177
|
-
|
178
|
-
if (error && typeof error === 'object' && 'message' in error) {
|
179
|
-
return (error as any).message || defaultMessage;
|
180
|
-
}
|
181
|
-
|
182
|
-
return defaultMessage;
|
183
|
-
}
|
184
|
-
|
185
|
-
/**
|
186
|
-
* Extract error details if available
|
187
|
-
*
|
188
|
-
* @param error The error to extract details from
|
189
|
-
* @returns The error details or undefined if none are found
|
190
|
-
*/
|
191
|
-
export function getErrorDetails(error: unknown): Record<string, any> | undefined {
|
192
|
-
if (error instanceof ApiError || error instanceof PlatformError) {
|
193
|
-
return error.details;
|
194
|
-
}
|
195
|
-
|
196
|
-
if (error && typeof error === 'object' && 'details' in error) {
|
197
|
-
return (error as any).details;
|
198
|
-
}
|
199
|
-
|
200
|
-
return undefined;
|
201
|
-
}
|
202
|
-
|
203
|
-
/**
|
204
|
-
* Enrich an error with additional context
|
205
|
-
*
|
206
|
-
* @param error The error to enrich
|
207
|
-
* @param context The context to add to the error
|
208
|
-
* @returns The enriched error
|
209
|
-
*/
|
210
|
-
export function enrichErrorWithContext(
|
211
|
-
error: unknown,
|
212
|
-
context: Record<string, any>,
|
213
|
-
): Error {
|
214
|
-
if (error instanceof ApiError) {
|
215
|
-
// Create a new ApiError with the merged details since details is read-only
|
216
|
-
return new ApiError(
|
217
|
-
error.message,
|
218
|
-
error.code,
|
219
|
-
error.status,
|
220
|
-
{ ...(error.details || {}), ...context },
|
221
|
-
error.recoverable,
|
222
|
-
);
|
223
|
-
}
|
224
|
-
|
225
|
-
if (error instanceof PlatformError) {
|
226
|
-
// Create a new PlatformError with the merged details since details is read-only
|
227
|
-
return new PlatformError(
|
228
|
-
error.message,
|
229
|
-
error.platform,
|
230
|
-
error.code,
|
231
|
-
error.recoverable,
|
232
|
-
error.originalError,
|
233
|
-
error.status,
|
234
|
-
error.userId,
|
235
|
-
{ ...(error.details || {}), ...context },
|
236
|
-
);
|
237
|
-
}
|
238
|
-
|
239
|
-
// For regular errors or non-Error objects, create a new ApiError with the context
|
240
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
241
|
-
return new ApiError(
|
242
|
-
errorMessage || 'An error occurred',
|
243
|
-
ApiErrorCode.INTERNAL_ERROR,
|
244
|
-
500,
|
245
|
-
{ originalError: error, ...context },
|
246
|
-
false,
|
247
|
-
);
|
248
|
-
}
|
249
|
-
|
250
|
-
/**
|
251
|
-
* Re-export the handleErrorResponse function from error.ts
|
252
|
-
* for backward compatibility
|
253
|
-
*/
|
254
|
-
export function handleErrorResponse(data: any, status: number): ApiError | PlatformError {
|
255
|
-
return baseHandleErrorResponse(data, status);
|
256
|
-
}
|
257
|
-
|
258
|
-
/**
|
259
|
-
* Wrapper for API calls with consistent error handling
|
260
|
-
*
|
261
|
-
* @param apiCall The API call to wrap
|
262
|
-
* @param context Optional context to add to any errors
|
263
|
-
* @returns The result of the API call
|
264
|
-
* @throws An enriched error if the API call fails
|
265
|
-
*/
|
266
|
-
export async function apiWrapper<T>(
|
267
|
-
apiCall: () => Promise<T>,
|
268
|
-
context?: Record<string, any>,
|
269
|
-
): Promise<T> {
|
270
|
-
try {
|
271
|
-
return await apiCall();
|
272
|
-
} catch (error) {
|
273
|
-
// If it's a Response object, use handleErrorResponse
|
274
|
-
if (error instanceof Response) {
|
275
|
-
try {
|
276
|
-
const errorData = await error.json();
|
277
|
-
throw enrichErrorWithContext(
|
278
|
-
handleErrorResponse(errorData, error.status),
|
279
|
-
context || {},
|
280
|
-
);
|
281
|
-
} catch (jsonError) {
|
282
|
-
// If JSON parsing fails, create a generic error
|
283
|
-
if (jsonError instanceof Error && jsonError.name === 'SyntaxError') {
|
284
|
-
throw enrichErrorWithContext(
|
285
|
-
new ApiError(
|
286
|
-
`API request failed with status ${error.status} and non-JSON response`,
|
287
|
-
ApiErrorCode.NETWORK_ERROR,
|
288
|
-
error.status as any,
|
289
|
-
{ originalResponse: error.statusText },
|
290
|
-
),
|
291
|
-
context || {},
|
292
|
-
);
|
293
|
-
}
|
294
|
-
// If it's already an enriched error from handleErrorResponse, just throw it
|
295
|
-
throw jsonError;
|
296
|
-
}
|
297
|
-
}
|
298
|
-
|
299
|
-
// If it's already an ApiError or PlatformError, just add context
|
300
|
-
if (error instanceof ApiError || error instanceof PlatformError) {
|
301
|
-
throw enrichErrorWithContext(error, context || {});
|
302
|
-
}
|
303
|
-
|
304
|
-
// Otherwise wrap it in an ApiError
|
305
|
-
throw enrichErrorWithContext(
|
306
|
-
error instanceof Error ? error : new Error(String(error)),
|
307
|
-
context || {},
|
308
|
-
);
|
309
|
-
}
|
310
|
-
}
|