b5-api-client 0.0.32 → 0.0.34

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.
@@ -58,7 +58,11 @@ class P2PMarketplaceAPIClient {
58
58
  }
59
59
  return response;
60
60
  }, (error) => __awaiter(this, void 0, void 0, function* () {
61
- var _a, _b;
61
+ var _a, _b, _c;
62
+ // Transform error response data to camelCase for consistency
63
+ if ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) {
64
+ error.response.data = toCamelCase(error.response.data);
65
+ }
62
66
  const { response, config } = error;
63
67
  if (!this.authTokenProvider || (response === null || response === void 0 ? void 0 : response.status) !== 401 || !config) {
64
68
  return Promise.reject(error);
@@ -73,12 +77,12 @@ class P2PMarketplaceAPIClient {
73
77
  if (!newToken) {
74
78
  return Promise.reject(error);
75
79
  }
76
- originalRequest.headers = (_a = originalRequest.headers) !== null && _a !== void 0 ? _a : {};
80
+ originalRequest.headers = (_b = originalRequest.headers) !== null && _b !== void 0 ? _b : {};
77
81
  originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
78
82
  return this.client(originalRequest);
79
83
  }
80
84
  catch (refreshError) {
81
- if ((_b = this.authTokenProvider) === null || _b === void 0 ? void 0 : _b.onRefreshFailure) {
85
+ if ((_c = this.authTokenProvider) === null || _c === void 0 ? void 0 : _c.onRefreshFailure) {
82
86
  this.authTokenProvider.onRefreshFailure(refreshError);
83
87
  }
84
88
  return Promise.reject(error);
@@ -122,27 +126,15 @@ class P2PMarketplaceAPIClient {
122
126
  get(url, headers) {
123
127
  return __awaiter(this, void 0, void 0, function* () {
124
128
  const config = this.createConfig(headers);
125
- try {
126
- const response = yield this.client.get(url, config);
127
- return response.data;
128
- }
129
- catch (error) {
130
- console.error(`GET request to ${url} failed:`, error);
131
- throw error;
132
- }
129
+ const response = yield this.client.get(url, config);
130
+ return response.data;
133
131
  });
134
132
  }
135
133
  post(url, data, headers) {
136
134
  return __awaiter(this, void 0, void 0, function* () {
137
135
  const config = this.createConfig(headers);
138
- try {
139
- const response = yield this.client.post(url, data, config);
140
- return response.data;
141
- }
142
- catch (error) {
143
- console.error(`POST request to ${url} failed:`, error);
144
- throw error;
145
- }
136
+ const response = yield this.client.post(url, data, config);
137
+ return response.data;
146
138
  });
147
139
  }
148
140
  getOrders(props, headers) {
@@ -287,13 +279,7 @@ class P2PMarketplaceAPIClient {
287
279
  isAdmin: true
288
280
  };
289
281
  const url = "api/orders/release";
290
- try {
291
- return yield this.post(url, body, headers);
292
- }
293
- catch (error) {
294
- console.error("Error releasing funds:", error);
295
- throw new Error("Failed to release funds");
296
- }
282
+ return yield this.post(url, body, headers);
297
283
  });
298
284
  }
299
285
  getTransactionStatus(txHash, headers) {
@@ -316,13 +302,7 @@ class P2PMarketplaceAPIClient {
316
302
  return __awaiter(this, void 0, void 0, function* () {
317
303
  const url = `/api/orders/${orderId}`;
318
304
  const config = this.createConfig(headers);
319
- try {
320
- yield this.client.delete(url, config);
321
- }
322
- catch (error) {
323
- console.error(`DELETE request to ${url} failed:`, error);
324
- throw error;
325
- }
305
+ yield this.client.delete(url, config);
326
306
  });
327
307
  }
328
308
  // OpsLog
@@ -367,14 +347,8 @@ class P2PMarketplaceAPIClient {
367
347
  return __awaiter(this, void 0, void 0, function* () {
368
348
  const url = '/api/session';
369
349
  const config = this.createConfig(headers);
370
- try {
371
- const response = yield this.client.delete(url, config);
372
- return response.data;
373
- }
374
- catch (error) {
375
- console.error(`DELETE request to ${url} failed:`, error);
376
- throw error;
377
- }
350
+ const response = yield this.client.delete(url, config);
351
+ return response.data;
378
352
  });
379
353
  }
380
354
  }
@@ -76,4 +76,5 @@ export declare class FirebaseService implements LoginService {
76
76
  private _createSuccessAuthResult;
77
77
  private getUserFromBackend;
78
78
  private createUserInBackend;
79
+ private getOrCreateUserInBackend;
79
80
  }
@@ -342,11 +342,9 @@ class FirebaseService {
342
342
  const userCredential = yield (0, auth_1.signInWithPopup)(auth, provider);
343
343
  const user = userCredential.user;
344
344
  const idToken = yield user.getIdToken();
345
- // Get or create user in your Kotlin backend
346
- const backendUser = yield this.getUserFromBackend(user.uid, idToken);
345
+ const backendUser = yield this.getOrCreateUserInBackend(user, idToken);
347
346
  if (backendUser) {
348
347
  const syncedBackendUser = yield this.ensureBackendVerification(user, backendUser, idToken);
349
- // Use the new helper method
350
348
  return this._createSuccessAuthResult(user, syncedBackendUser, AuthProvider.GOOGLE, idToken);
351
349
  }
352
350
  else {
@@ -580,5 +578,19 @@ class FirebaseService {
580
578
  }
581
579
  });
582
580
  }
581
+ getOrCreateUserInBackend(user, idToken) {
582
+ var _a;
583
+ return __awaiter(this, void 0, void 0, function* () {
584
+ // Try to get existing user from backend
585
+ const existingUser = yield this.getUserFromBackend(user.uid, idToken);
586
+ if (existingUser) {
587
+ return existingUser;
588
+ }
589
+ // User doesn't exist, create them (first-time social sign-in)
590
+ const username = ((_a = user.email) === null || _a === void 0 ? void 0 : _a.split('@')[0])
591
+ || `user_${user.uid.substring(0, 8)}`;
592
+ return this.createUserInBackend(user.uid, username, idToken);
593
+ });
594
+ }
583
595
  }
584
596
  exports.FirebaseService = FirebaseService;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Validation error codes returned by the backend API.
3
+ * These correspond to the ValidationError enum in the Kotlin backend.
4
+ */
5
+ export type ValidationError = 'USER_HAS_ACTIVE_ORDERS_AS_CREATOR' | 'USER_HAS_TOO_MANY_PENDING_ORDERS' | 'ORDER_AMOUNT_TOO_LOW' | 'ORDER_AMOUNT_TOO_HIGH' | 'USER_HAS_ACTIVE_ORDERS' | 'ORDER_NOT_FOUND' | 'ORDER_CANCELLATION_NOT_ALLOWED' | 'INVALID_REQUEST';
6
+ /**
7
+ * Structure of the error response from the backend API.
8
+ */
9
+ export interface ApiErrorResponse {
10
+ message: string;
11
+ code?: number;
12
+ error?: ValidationError;
13
+ }
14
+ /**
15
+ * Type guard to check if an error response has a validation error.
16
+ */
17
+ export declare function hasValidationError(error: ApiErrorResponse): error is ApiErrorResponse & {
18
+ error: ValidationError;
19
+ };
20
+ /**
21
+ * Parses an error (typically from an axios request) into a typed ApiErrorResponse.
22
+ * This is a non-breaking helper that works with existing error handling code.
23
+ *
24
+ * @param error - The error to parse (usually from a catch block)
25
+ * @returns ApiErrorResponse with extracted message, code, and error type
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * try {
30
+ * await client.createOrder(order, headers);
31
+ * } catch (err) {
32
+ * const apiError = parseApiError(err);
33
+ * if (apiError.error === 'ORDER_AMOUNT_TOO_LOW') {
34
+ * // Show specific message for this error
35
+ * }
36
+ * }
37
+ * ```
38
+ */
39
+ export declare function parseApiError(error: unknown): ApiErrorResponse;
package/dist/errors.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseApiError = exports.hasValidationError = void 0;
4
+ /**
5
+ * Type guard to check if an error response has a validation error.
6
+ */
7
+ function hasValidationError(error) {
8
+ return error.error !== undefined;
9
+ }
10
+ exports.hasValidationError = hasValidationError;
11
+ /**
12
+ * Parses an error (typically from an axios request) into a typed ApiErrorResponse.
13
+ * This is a non-breaking helper that works with existing error handling code.
14
+ *
15
+ * @param error - The error to parse (usually from a catch block)
16
+ * @returns ApiErrorResponse with extracted message, code, and error type
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * try {
21
+ * await client.createOrder(order, headers);
22
+ * } catch (err) {
23
+ * const apiError = parseApiError(err);
24
+ * if (apiError.error === 'ORDER_AMOUNT_TOO_LOW') {
25
+ * // Show specific message for this error
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ function parseApiError(error) {
31
+ var _a;
32
+ // Handle axios errors with response data
33
+ if (isAxiosError(error) && ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data)) {
34
+ const data = error.response.data;
35
+ return {
36
+ message: data.message || error.message,
37
+ code: data.code,
38
+ error: data.error
39
+ };
40
+ }
41
+ // Handle standard Error objects
42
+ if (error instanceof Error) {
43
+ return { message: error.message };
44
+ }
45
+ // Handle string errors
46
+ if (typeof error === 'string') {
47
+ return { message: error };
48
+ }
49
+ // Fallback for unknown error types
50
+ return { message: 'An unexpected error occurred' };
51
+ }
52
+ exports.parseApiError = parseApiError;
53
+ /**
54
+ * Type guard to check if an error is an AxiosError.
55
+ */
56
+ function isAxiosError(error) {
57
+ return (error !== null &&
58
+ typeof error === 'object' &&
59
+ 'isAxiosError' in error &&
60
+ error.isAxiosError === true);
61
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { default as P2PMarketplaceAPIClient } from './P2PMarketplaceAPIClient';
2
2
  export { generateSecretAndHash } from './cryptoUtils';
3
3
  export * from './types';
4
+ export * from './errors';
4
5
  export { FirebaseService, AuthProvider, UserData, AuthResult } from './auth/FirebaseLoginService';
5
6
  export { createLoginService } from './auth/LoginService';
6
7
  export { userContext } from './auth/UserContext';
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ Object.defineProperty(exports, "P2PMarketplaceAPIClient", { enumerable: true, ge
23
23
  var cryptoUtils_1 = require("./cryptoUtils");
24
24
  Object.defineProperty(exports, "generateSecretAndHash", { enumerable: true, get: function () { return cryptoUtils_1.generateSecretAndHash; } });
25
25
  __exportStar(require("./types"), exports);
26
+ __exportStar(require("./errors"), exports);
26
27
  var FirebaseLoginService_1 = require("./auth/FirebaseLoginService");
27
28
  Object.defineProperty(exports, "FirebaseService", { enumerable: true, get: function () { return FirebaseLoginService_1.FirebaseService; } });
28
29
  Object.defineProperty(exports, "AuthProvider", { enumerable: true, get: function () { return FirebaseLoginService_1.AuthProvider; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "b5-api-client",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "description": "Escrow Backend API client",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -59,6 +59,11 @@ class P2PMarketplaceAPIClient {
59
59
  }
60
60
  return response;
61
61
  }, async (error: AxiosError) => {
62
+ // Transform error response data to camelCase for consistency
63
+ if (error.response?.data) {
64
+ error.response.data = toCamelCase(error.response.data);
65
+ }
66
+
62
67
  const { response, config } = error;
63
68
  if (!this.authTokenProvider || response?.status !== 401 || !config) {
64
69
  return Promise.reject(error);
@@ -128,24 +133,14 @@ class P2PMarketplaceAPIClient {
128
133
 
129
134
  async get<T>(url: string, headers?: Record<string, string>): Promise<T> {
130
135
  const config = this.createConfig(headers);
131
- try {
132
- const response = await this.client.get<T>(url, config);
133
- return response.data;
134
- } catch (error) {
135
- console.error(`GET request to ${url} failed:`, error);
136
- throw error;
137
- }
136
+ const response = await this.client.get<T>(url, config);
137
+ return response.data;
138
138
  }
139
139
 
140
140
  async post<T>(url: string, data: any, headers?: Record<string, string>): Promise<T> {
141
141
  const config = this.createConfig(headers);
142
- try {
143
- const response = await this.client.post<T>(url, data, config);
144
- return response.data;
145
- } catch (error) {
146
- console.error(`POST request to ${url} failed:`, error);
147
- throw error;
148
- }
142
+ const response = await this.client.post<T>(url, data, config);
143
+ return response.data;
149
144
  }
150
145
 
151
146
  public async getOrders(props: GetOrdersParams, headers?: Record<string, string>): Promise<OrderResponse> {
@@ -273,12 +268,7 @@ class P2PMarketplaceAPIClient {
273
268
  };
274
269
 
275
270
  const url = "api/orders/release"
276
- try {
277
- return await this.post<string>(url, body, headers);
278
- } catch (error) {
279
- console.error("Error releasing funds:", error);
280
- throw new Error("Failed to release funds");
281
- }
271
+ return await this.post<string>(url, body, headers);
282
272
  }
283
273
 
284
274
  public async getTransactionStatus(txHash: string, headers?: Record<string, string>): Promise<TransactionStatusResponse> {
@@ -298,12 +288,7 @@ class P2PMarketplaceAPIClient {
298
288
  public async cancelOrder(orderId: string, headers?: Record<string, string>): Promise<void> {
299
289
  const url = `/api/orders/${orderId}`;
300
290
  const config = this.createConfig(headers);
301
- try {
302
- await this.client.delete(url, config);
303
- } catch (error) {
304
- console.error(`DELETE request to ${url} failed:`, error);
305
- throw error;
306
- }
291
+ await this.client.delete(url, config);
307
292
  }
308
293
 
309
294
  // OpsLog
@@ -346,13 +331,8 @@ class P2PMarketplaceAPIClient {
346
331
  public async clearSession(headers?: Record<string, string>): Promise<{ success: boolean }> {
347
332
  const url = '/api/session';
348
333
  const config = this.createConfig(headers);
349
- try {
350
- const response = await this.client.delete<{ success: boolean }>(url, config);
351
- return response.data;
352
- } catch (error) {
353
- console.error(`DELETE request to ${url} failed:`, error);
354
- throw error;
355
- }
334
+ const response = await this.client.delete<{ success: boolean }>(url, config);
335
+ return response.data;
356
336
  }
357
337
 
358
338
  }
@@ -420,11 +420,9 @@ export class FirebaseService implements LoginService {
420
420
  const user = userCredential.user;
421
421
  const idToken = await user.getIdToken();
422
422
 
423
- // Get or create user in your Kotlin backend
424
- const backendUser = await this.getUserFromBackend(user.uid, idToken);
423
+ const backendUser = await this.getOrCreateUserInBackend(user, idToken);
425
424
  if (backendUser) {
426
425
  const syncedBackendUser = await this.ensureBackendVerification(user, backendUser, idToken);
427
- // Use the new helper method
428
426
  return this._createSuccessAuthResult(user, syncedBackendUser, AuthProvider.GOOGLE, idToken);
429
427
  } else {
430
428
  return AuthResult.failure('Failed to get or create user in backend');
@@ -663,4 +661,21 @@ export class FirebaseService implements LoginService {
663
661
  return null;
664
662
  }
665
663
  }
664
+
665
+ private async getOrCreateUserInBackend(
666
+ user: User,
667
+ idToken: string
668
+ ): Promise<KioscoinUser | null> {
669
+ // Try to get existing user from backend
670
+ const existingUser = await this.getUserFromBackend(user.uid, idToken);
671
+ if (existingUser) {
672
+ return existingUser;
673
+ }
674
+
675
+ // User doesn't exist, create them (first-time social sign-in)
676
+ const username = user.email?.split('@')[0]
677
+ || `user_${user.uid.substring(0, 8)}`;
678
+
679
+ return this.createUserInBackend(user.uid, username, idToken);
680
+ }
666
681
  }
package/src/errors.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { AxiosError } from 'axios';
2
+
3
+ /**
4
+ * Validation error codes returned by the backend API.
5
+ * These correspond to the ValidationError enum in the Kotlin backend.
6
+ */
7
+ export type ValidationError =
8
+ // Order creation errors
9
+ | 'USER_HAS_ACTIVE_ORDERS_AS_CREATOR'
10
+ | 'USER_HAS_TOO_MANY_PENDING_ORDERS'
11
+ | 'ORDER_AMOUNT_TOO_LOW'
12
+ | 'ORDER_AMOUNT_TOO_HIGH'
13
+ // Order taking errors
14
+ | 'USER_HAS_ACTIVE_ORDERS'
15
+ // Order cancellation errors
16
+ | 'ORDER_NOT_FOUND'
17
+ | 'ORDER_CANCELLATION_NOT_ALLOWED'
18
+ // Generic
19
+ | 'INVALID_REQUEST';
20
+
21
+ /**
22
+ * Structure of the error response from the backend API.
23
+ */
24
+ export interface ApiErrorResponse {
25
+ message: string;
26
+ code?: number;
27
+ error?: ValidationError;
28
+ }
29
+
30
+ /**
31
+ * Type guard to check if an error response has a validation error.
32
+ */
33
+ export function hasValidationError(error: ApiErrorResponse): error is ApiErrorResponse & { error: ValidationError } {
34
+ return error.error !== undefined;
35
+ }
36
+
37
+ /**
38
+ * Parses an error (typically from an axios request) into a typed ApiErrorResponse.
39
+ * This is a non-breaking helper that works with existing error handling code.
40
+ *
41
+ * @param error - The error to parse (usually from a catch block)
42
+ * @returns ApiErrorResponse with extracted message, code, and error type
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * try {
47
+ * await client.createOrder(order, headers);
48
+ * } catch (err) {
49
+ * const apiError = parseApiError(err);
50
+ * if (apiError.error === 'ORDER_AMOUNT_TOO_LOW') {
51
+ * // Show specific message for this error
52
+ * }
53
+ * }
54
+ * ```
55
+ */
56
+ export function parseApiError(error: unknown): ApiErrorResponse {
57
+ // Handle axios errors with response data
58
+ if (isAxiosError(error) && error.response?.data) {
59
+ const data = error.response.data as ApiErrorResponse;
60
+ return {
61
+ message: data.message || error.message,
62
+ code: data.code,
63
+ error: data.error
64
+ };
65
+ }
66
+
67
+ // Handle standard Error objects
68
+ if (error instanceof Error) {
69
+ return { message: error.message };
70
+ }
71
+
72
+ // Handle string errors
73
+ if (typeof error === 'string') {
74
+ return { message: error };
75
+ }
76
+
77
+ // Fallback for unknown error types
78
+ return { message: 'An unexpected error occurred' };
79
+ }
80
+
81
+ /**
82
+ * Type guard to check if an error is an AxiosError.
83
+ */
84
+ function isAxiosError(error: unknown): error is AxiosError {
85
+ return (
86
+ error !== null &&
87
+ typeof error === 'object' &&
88
+ 'isAxiosError' in error &&
89
+ (error as AxiosError).isAxiosError === true
90
+ );
91
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { default as P2PMarketplaceAPIClient } from './P2PMarketplaceAPIClient';
2
2
  export { generateSecretAndHash } from './cryptoUtils'
3
3
  export * from './types';
4
+ export * from './errors';
4
5
  export { FirebaseService, AuthProvider, UserData, AuthResult } from './auth/FirebaseLoginService';
5
6
  export { createLoginService } from './auth/LoginService';
6
7
  export { userContext } from './auth/UserContext';