@oxyhq/services 5.13.25 → 5.13.26

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.
Files changed (85) hide show
  1. package/lib/commonjs/core/HttpService.js +481 -0
  2. package/lib/commonjs/core/HttpService.js.map +1 -0
  3. package/lib/commonjs/core/OxyServices.base.js +29 -26
  4. package/lib/commonjs/core/OxyServices.base.js.map +1 -1
  5. package/lib/commonjs/core/OxyServices.js +1 -2
  6. package/lib/commonjs/core/OxyServices.js.map +1 -1
  7. package/lib/commonjs/core/mixins/OxyServices.assets.js +3 -2
  8. package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -1
  9. package/lib/commonjs/core/mixins/OxyServices.user.js +9 -5
  10. package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
  11. package/lib/commonjs/core/mixins/OxyServices.utility.js +1 -0
  12. package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -1
  13. package/lib/commonjs/utils/errorUtils.js +35 -15
  14. package/lib/commonjs/utils/errorUtils.js.map +1 -1
  15. package/lib/module/core/HttpService.js +476 -0
  16. package/lib/module/core/HttpService.js.map +1 -0
  17. package/lib/module/core/OxyServices.base.js +29 -26
  18. package/lib/module/core/OxyServices.base.js.map +1 -1
  19. package/lib/module/core/OxyServices.js +1 -2
  20. package/lib/module/core/OxyServices.js.map +1 -1
  21. package/lib/module/core/mixins/OxyServices.assets.js +3 -2
  22. package/lib/module/core/mixins/OxyServices.assets.js.map +1 -1
  23. package/lib/module/core/mixins/OxyServices.user.js +9 -5
  24. package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
  25. package/lib/module/core/mixins/OxyServices.utility.js +1 -0
  26. package/lib/module/core/mixins/OxyServices.utility.js.map +1 -1
  27. package/lib/module/utils/errorUtils.js +36 -15
  28. package/lib/module/utils/errorUtils.js.map +1 -1
  29. package/lib/typescript/core/HttpService.d.ts +111 -0
  30. package/lib/typescript/core/HttpService.d.ts.map +1 -0
  31. package/lib/typescript/core/OxyServices.base.d.ts +6 -8
  32. package/lib/typescript/core/OxyServices.base.d.ts.map +1 -1
  33. package/lib/typescript/core/OxyServices.d.ts +1 -2
  34. package/lib/typescript/core/OxyServices.d.ts.map +1 -1
  35. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts +4 -5
  36. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -1
  37. package/lib/typescript/core/mixins/OxyServices.assets.d.ts +4 -5
  38. package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
  39. package/lib/typescript/core/mixins/OxyServices.auth.d.ts +4 -5
  40. package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -1
  41. package/lib/typescript/core/mixins/OxyServices.developer.d.ts +4 -5
  42. package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -1
  43. package/lib/typescript/core/mixins/OxyServices.devices.d.ts +4 -5
  44. package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -1
  45. package/lib/typescript/core/mixins/OxyServices.karma.d.ts +4 -5
  46. package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -1
  47. package/lib/typescript/core/mixins/OxyServices.language.d.ts +4 -5
  48. package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -1
  49. package/lib/typescript/core/mixins/OxyServices.location.d.ts +4 -5
  50. package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -1
  51. package/lib/typescript/core/mixins/OxyServices.payment.d.ts +4 -5
  52. package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -1
  53. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts +4 -5
  54. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -1
  55. package/lib/typescript/core/mixins/OxyServices.totp.d.ts +4 -5
  56. package/lib/typescript/core/mixins/OxyServices.totp.d.ts.map +1 -1
  57. package/lib/typescript/core/mixins/OxyServices.user.d.ts +4 -5
  58. package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
  59. package/lib/typescript/core/mixins/OxyServices.utility.d.ts +4 -5
  60. package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -1
  61. package/lib/typescript/core/mixins/index.d.ts +52 -65
  62. package/lib/typescript/core/mixins/index.d.ts.map +1 -1
  63. package/lib/typescript/utils/errorUtils.d.ts.map +1 -1
  64. package/package.json +1 -1
  65. package/src/core/HttpService.ts +523 -0
  66. package/src/core/OxyServices.base.ts +36 -34
  67. package/src/core/OxyServices.ts +1 -2
  68. package/src/core/mixins/OxyServices.assets.ts +2 -1
  69. package/src/core/mixins/OxyServices.user.ts +7 -6
  70. package/src/core/mixins/OxyServices.utility.ts +1 -0
  71. package/src/utils/errorUtils.ts +65 -19
  72. package/lib/commonjs/core/HttpClient.js +0 -317
  73. package/lib/commonjs/core/HttpClient.js.map +0 -1
  74. package/lib/commonjs/core/RequestManager.js +0 -170
  75. package/lib/commonjs/core/RequestManager.js.map +0 -1
  76. package/lib/module/core/HttpClient.js +0 -311
  77. package/lib/module/core/HttpClient.js.map +0 -1
  78. package/lib/module/core/RequestManager.js +0 -165
  79. package/lib/module/core/RequestManager.js.map +0 -1
  80. package/lib/typescript/core/HttpClient.d.ts +0 -110
  81. package/lib/typescript/core/HttpClient.d.ts.map +0 -1
  82. package/lib/typescript/core/RequestManager.d.ts +0 -63
  83. package/lib/typescript/core/RequestManager.d.ts.map +0 -1
  84. package/src/core/HttpClient.ts +0 -346
  85. package/src/core/RequestManager.ts +0 -205
@@ -6,8 +6,7 @@
6
6
  import { jwtDecode } from 'jwt-decode';
7
7
  import type { OxyConfig as OxyConfigBase, ApiError, User } from '../models/interfaces';
8
8
  import { handleHttpError } from '../utils/errorUtils';
9
- import { HttpClient } from './HttpClient';
10
- import { RequestManager, type RequestOptions } from './RequestManager';
9
+ import { HttpService, type RequestOptions } from './HttpService';
11
10
  import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors';
12
11
 
13
12
  export interface OxyConfig extends OxyConfigBase {
@@ -26,8 +25,7 @@ interface JwtPayload {
26
25
  * Base class for OxyServices with core infrastructure
27
26
  */
28
27
  export class OxyServicesBase {
29
- public httpClient: HttpClient;
30
- public requestManager: RequestManager;
28
+ public httpService: HttpService;
31
29
  public cloudURL: string;
32
30
  public config: OxyConfig;
33
31
 
@@ -39,16 +37,13 @@ export class OxyServicesBase {
39
37
  this.config = config;
40
38
  this.cloudURL = config.cloudURL || 'https://cloud.oxy.so';
41
39
 
42
- // Initialize HTTP client (handles authentication and interceptors)
43
- this.httpClient = new HttpClient(config);
44
-
45
- // Initialize request manager (handles caching, deduplication, queuing, retry)
46
- this.requestManager = new RequestManager(this.httpClient, config);
40
+ // Initialize unified HTTP service (handles auth, caching, deduplication, queuing, retry)
41
+ this.httpService = new HttpService(config);
47
42
  }
48
43
 
49
44
  // Test-only utility to reset global tokens between jest tests
50
45
  static __resetTokensForTests(): void {
51
- HttpClient.__resetTokensForTests();
46
+ HttpService.__resetTokensForTests();
52
47
  }
53
48
 
54
49
  /**
@@ -61,7 +56,13 @@ export class OxyServicesBase {
61
56
  data?: any,
62
57
  options: RequestOptions = {}
63
58
  ): Promise<T> {
64
- return this.requestManager.request<T>(method, url, data, options);
59
+ return this.httpService.request<T>({
60
+ method,
61
+ url,
62
+ data: method !== 'GET' ? data : undefined,
63
+ params: method === 'GET' ? data : undefined,
64
+ ...options,
65
+ });
65
66
  }
66
67
 
67
68
  // ============================================================================
@@ -72,43 +73,43 @@ export class OxyServicesBase {
72
73
  * Get the configured Oxy API base URL
73
74
  */
74
75
  public getBaseURL(): string {
75
- return this.httpClient.getBaseURL();
76
+ return this.httpService.getBaseURL();
76
77
  }
77
78
 
78
79
  /**
79
- * Get the HTTP client instance
80
- * Useful for advanced use cases where direct access to the HTTP client is needed
80
+ * Get the HTTP service instance
81
+ * Useful for advanced use cases where direct access to the HTTP service is needed
81
82
  */
82
- public getClient(): HttpClient {
83
- return this.httpClient;
83
+ public getClient(): HttpService {
84
+ return this.httpService;
84
85
  }
85
86
 
86
87
  /**
87
88
  * Get performance metrics
88
89
  */
89
90
  public getMetrics() {
90
- return this.requestManager.getMetrics();
91
+ return this.httpService.getMetrics();
91
92
  }
92
93
 
93
94
  /**
94
95
  * Clear request cache
95
96
  */
96
97
  public clearCache(): void {
97
- this.requestManager.clearCache();
98
+ this.httpService.clearCache();
98
99
  }
99
100
 
100
101
  /**
101
102
  * Clear specific cache entry
102
103
  */
103
104
  public clearCacheEntry(key: string): void {
104
- this.requestManager.clearCacheEntry(key);
105
+ this.httpService.clearCacheEntry(key);
105
106
  }
106
107
 
107
108
  /**
108
109
  * Get cache statistics
109
110
  */
110
111
  public getCacheStats() {
111
- return this.requestManager.getCacheStats();
112
+ return this.httpService.getCacheStats();
112
113
  }
113
114
 
114
115
  /**
@@ -122,21 +123,21 @@ export class OxyServicesBase {
122
123
  * Set authentication tokens
123
124
  */
124
125
  public setTokens(accessToken: string, refreshToken = ''): void {
125
- this.httpClient.setTokens(accessToken, refreshToken);
126
+ this.httpService.setTokens(accessToken, refreshToken);
126
127
  }
127
128
 
128
129
  /**
129
130
  * Clear stored authentication tokens
130
131
  */
131
132
  public clearTokens(): void {
132
- this.httpClient.clearTokens();
133
+ this.httpService.clearTokens();
133
134
  }
134
135
 
135
136
  /**
136
137
  * Get the current user ID from the access token
137
138
  */
138
139
  public getCurrentUserId(): string | null {
139
- const accessToken = this.httpClient.getAccessToken();
140
+ const accessToken = this.httpService.getAccessToken();
140
141
  if (!accessToken) {
141
142
  return null;
142
143
  }
@@ -153,14 +154,14 @@ export class OxyServicesBase {
153
154
  * Check if the client has a valid access token (public method)
154
155
  */
155
156
  public hasValidToken(): boolean {
156
- return this.httpClient.hasAccessToken();
157
+ return this.httpService.hasAccessToken();
157
158
  }
158
159
 
159
160
  /**
160
161
  * Get the raw access token (for constructing anchor URLs when needed)
161
162
  */
162
163
  public getAccessToken(): string | null {
163
- return this.httpClient.getAccessToken();
164
+ return this.httpService.getAccessToken();
164
165
  }
165
166
 
166
167
  /**
@@ -183,7 +184,7 @@ export class OxyServicesBase {
183
184
  */
184
185
  public async waitForAuth(timeoutMs = 5000): Promise<boolean> {
185
186
  // Immediate synchronous check - no delay if token is ready
186
- if (this.httpClient.hasAccessToken()) {
187
+ if (this.httpService.hasAccessToken()) {
187
188
  return true;
188
189
  }
189
190
 
@@ -196,7 +197,7 @@ export class OxyServicesBase {
196
197
  while (performance.now() < maxTime) {
197
198
  await new Promise(resolve => setTimeout(resolve, pollInterval));
198
199
 
199
- if (this.httpClient.hasAccessToken()) {
200
+ if (this.httpService.hasAccessToken()) {
200
201
  return true;
201
202
  }
202
203
 
@@ -232,7 +233,7 @@ export class OxyServicesBase {
232
233
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
233
234
  try {
234
235
  // First attempt: check if we have a token
235
- if (!this.httpClient.hasAccessToken()) {
236
+ if (!this.httpService.hasAccessToken()) {
236
237
  if (attempt === 0) {
237
238
  // On first attempt, wait briefly for authentication to complete
238
239
  const authReady = await this.waitForAuth(authTimeoutMs);
@@ -252,11 +253,12 @@ export class OxyServicesBase {
252
253
  // Execute the operation
253
254
  return await operation();
254
255
 
255
- } catch (error: any) {
256
+ } catch (error: unknown) {
256
257
  const isLastAttempt = attempt === maxRetries;
257
- const isAuthError = error?.response?.status === 401 ||
258
- error?.code === 'MISSING_TOKEN' ||
259
- error?.message?.includes('Authentication') ||
258
+ const errorObj = error && typeof error === 'object' ? error as { response?: { status?: number }; code?: string; message?: string } : null;
259
+ const isAuthError = errorObj?.response?.status === 401 ||
260
+ errorObj?.code === 'MISSING_TOKEN' ||
261
+ errorObj?.message?.includes('Authentication') ||
260
262
  error instanceof OxyAuthenticationError;
261
263
 
262
264
  if (isAuthError && !isLastAttempt && !(error instanceof OxyAuthenticationTimeoutError)) {
@@ -298,12 +300,12 @@ export class OxyServicesBase {
298
300
  /**
299
301
  * Centralized error handling
300
302
  */
301
- public handleError(error: any): Error {
303
+ public handleError(error: unknown): Error {
302
304
  const api = handleHttpError(error);
303
305
  const err = new Error(api.message) as Error & { code?: string; status?: number; details?: Record<string, unknown> };
304
306
  err.code = api.code;
305
307
  err.status = api.status;
306
- err.details = api.details as any;
308
+ err.details = api.details;
307
309
  return err;
308
310
  }
309
311
 
@@ -68,8 +68,7 @@ import { composeOxyServices } from './mixins';
68
68
  * This class provides all API functionality in one simple, easy-to-use interface.
69
69
  *
70
70
  * ## Architecture
71
- * - **HttpClient**: Handles HTTP communication and authentication
72
- * - **RequestManager**: Handles caching, deduplication, queuing, and retry
71
+ * - **HttpService**: Unified HTTP service handling authentication, caching, deduplication, queuing, and retry
73
72
  * - **OxyServices**: Provides high-level API methods
74
73
  *
75
74
  * ## Mixin Composition
@@ -235,11 +235,12 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
235
235
  // Fallback: direct upload via API to avoid CORS issues
236
236
  const fd = new FormData();
237
237
  fd.append('file', file);
238
- // Use httpClient directly for FormData uploads (bypasses RequestManager for special handling)
238
+ // Use httpService directly for FormData uploads (bypasses caching for special handling)
239
239
  await this.getClient().request({
240
240
  method: 'POST',
241
241
  url: `/api/assets/${encodeURIComponent(initResponse.fileId)}/upload-direct`,
242
242
  data: fd,
243
+ cache: false,
243
244
  });
244
245
  }
245
246
 
@@ -189,14 +189,15 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
189
189
  */
190
190
  async downloadAccountData(format: 'json' | 'csv' = 'json'): Promise<Blob> {
191
191
  try {
192
- // Use axios instance directly for blob responses since RequestManager doesn't handle blobs
193
- const axiosInstance = this.getClient().getAxiosInstance();
194
-
195
- const response = await axiosInstance.get(`/api/users/me/data?format=${format}`, {
196
- responseType: 'blob',
192
+ // Use httpService for blob responses (it handles blob responses automatically)
193
+ const result = await this.getClient().request<Blob>({
194
+ method: 'GET',
195
+ url: `/api/users/me/data`,
196
+ params: { format },
197
+ cache: false,
197
198
  });
198
199
 
199
- return response.data as Blob;
200
+ return result;
200
201
  } catch (error) {
201
202
  throw this.handleError(error);
202
203
  }
@@ -177,6 +177,7 @@ export function OxyServicesUtilityMixin<T extends typeof OxyServicesBase>(Base:
177
177
  const apiError = this.handleError(error) as any;
178
178
 
179
179
  if (debug) {
180
+ // Debug logging - using console.log is acceptable here for development
180
181
  console.log(`❌ Auth: Unexpected error:`, apiError);
181
182
  }
182
183
 
@@ -1,4 +1,5 @@
1
1
  import type { ApiError } from '../models/interfaces';
2
+ import { logger } from './loggerUtils';
2
3
 
3
4
  /**
4
5
  * Error handling utilities for consistent error processing
@@ -61,32 +62,72 @@ export function handleHttpError(error: unknown): ApiError {
61
62
  return error as ApiError;
62
63
  }
63
64
 
64
- // Handle axios errors - check if it looks like an axios error
65
- if (error && typeof error === 'object' && 'response' in error) {
66
- const axiosError = error as { response?: { status: number; data?: { message?: string; code?: string } } };
67
- if (axiosError.response) {
68
- const { status, data } = axiosError.response;
69
-
65
+ // Handle AbortError (timeout or cancelled requests)
66
+ if (error instanceof Error && error.name === 'AbortError') {
67
+ return createApiError(
68
+ 'Request timeout or cancelled',
69
+ ErrorCodes.TIMEOUT,
70
+ 0
71
+ );
72
+ }
73
+
74
+ // Handle TypeError (network failures, CORS, etc.)
75
+ if (error instanceof TypeError) {
76
+ // Check if it's a network-related TypeError
77
+ if (error.message.includes('fetch') || error.message.includes('network') || error.message.includes('Failed to fetch')) {
70
78
  return createApiError(
71
- data?.message || `HTTP ${status} error`,
72
- data?.code || getErrorCodeFromStatus(status),
73
- status,
74
- data
79
+ 'Network error - failed to connect to server',
80
+ ErrorCodes.NETWORK_ERROR,
81
+ 0
75
82
  );
76
83
  }
77
- }
78
-
79
- // Handle network errors - check if it looks like a network error
80
- if (error && typeof error === 'object' && 'request' in error) {
81
84
  return createApiError(
82
- 'Network error - no response received',
85
+ error.message || 'Network error occurred',
83
86
  ErrorCodes.NETWORK_ERROR,
84
87
  0
85
88
  );
86
89
  }
87
90
 
91
+ // Handle fetch Response errors - check if it has response property with status
92
+ if (error && typeof error === 'object' && 'response' in error) {
93
+ const fetchError = error as {
94
+ response?: {
95
+ status: number;
96
+ statusText?: string;
97
+ };
98
+ status?: number;
99
+ message?: string;
100
+ };
101
+
102
+ const status = fetchError.response?.status || fetchError.status;
103
+ if (status) {
104
+ return createApiError(
105
+ fetchError.message || `HTTP ${status} error`,
106
+ getErrorCodeFromStatus(status),
107
+ status
108
+ );
109
+ }
110
+ }
111
+
88
112
  // Handle standard errors
89
113
  if (error instanceof Error) {
114
+ // Check for common error patterns
115
+ if (error.message.includes('timeout') || error.message.includes('aborted')) {
116
+ return createApiError(
117
+ 'Request timeout',
118
+ ErrorCodes.TIMEOUT,
119
+ 0
120
+ );
121
+ }
122
+
123
+ if (error.message.includes('network') || error.message.includes('fetch')) {
124
+ return createApiError(
125
+ error.message || 'Network error occurred',
126
+ ErrorCodes.NETWORK_ERROR,
127
+ 0
128
+ );
129
+ }
130
+
90
131
  return createApiError(
91
132
  error.message || 'Unknown error occurred',
92
133
  ErrorCodes.INTERNAL_ERROR,
@@ -148,12 +189,17 @@ export function validateRequiredFields(data: Record<string, unknown>, fields: st
148
189
  * Safe error logging with context
149
190
  */
150
191
  export function logError(error: unknown, context?: string): void {
151
- const prefix = context ? `[${context}]` : '[Error]';
152
-
153
192
  if (error instanceof Error) {
154
- console.error(`${prefix} ${error.message}`, error.stack);
193
+ logger.error(error.message, {
194
+ component: context || 'errorUtils',
195
+ method: 'logError',
196
+ stack: error.stack,
197
+ });
155
198
  } else {
156
- console.error(`${prefix}`, error);
199
+ logger.error(String(error), {
200
+ component: context || 'errorUtils',
201
+ method: 'logError',
202
+ });
157
203
  }
158
204
  }
159
205
 
@@ -1,317 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.HttpClient = void 0;
7
- var _axios = _interopRequireDefault(require("axios"));
8
- var _jwtDecode = require("jwt-decode");
9
- var _errorUtils = require("../utils/errorUtils");
10
- var _requestUtils = require("../utils/requestUtils");
11
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
- /**
13
- * HTTP Client Service
14
- *
15
- * Handles all HTTP communication with authentication, interceptors, and error handling.
16
- * This is the single source of truth for making authenticated HTTP requests.
17
- */
18
-
19
- /**
20
- * Token store for authentication
21
- */
22
- class TokenStore {
23
- accessToken = null;
24
- refreshToken = null;
25
- constructor() {}
26
- static getInstance() {
27
- if (!TokenStore.instance) {
28
- TokenStore.instance = new TokenStore();
29
- }
30
- return TokenStore.instance;
31
- }
32
- setTokens(accessToken, refreshToken = '') {
33
- this.accessToken = accessToken;
34
- this.refreshToken = refreshToken;
35
- }
36
- getAccessToken() {
37
- return this.accessToken;
38
- }
39
- getRefreshToken() {
40
- return this.refreshToken;
41
- }
42
- clearTokens() {
43
- this.accessToken = null;
44
- this.refreshToken = null;
45
- }
46
- hasAccessToken() {
47
- return !!this.accessToken;
48
- }
49
- }
50
-
51
- /**
52
- * HTTP Client Service
53
- *
54
- * Manages Axios instance with authentication interceptors.
55
- * All HTTP requests should go through this service to ensure authentication.
56
- */
57
- class HttpClient {
58
- constructor(config) {
59
- this.baseURL = config.baseURL;
60
- this.tokenStore = TokenStore.getInstance();
61
- this.logger = new _requestUtils.SimpleLogger(config.enableLogging || false, config.logLevel || 'error', 'HttpClient');
62
- const timeout = config.requestTimeout || 5000;
63
-
64
- // Create Axios instance with optimized configuration
65
- this.client = _axios.default.create({
66
- baseURL: config.baseURL,
67
- timeout,
68
- headers: {
69
- 'Accept': 'application/json'
70
- },
71
- // Enable HTTP keep-alive for connection reuse (Node.js only)
72
- ...(typeof process !== 'undefined' && process.env && typeof window === 'undefined' && typeof require !== 'undefined' ? {
73
- httpAgent: new (require('http').Agent)({
74
- keepAlive: true,
75
- keepAliveMsecs: 1000,
76
- maxSockets: 50
77
- }),
78
- httpsAgent: new (require('https').Agent)({
79
- keepAlive: true,
80
- keepAliveMsecs: 1000,
81
- maxSockets: 50
82
- })
83
- } : {})
84
- });
85
- this.setupInterceptors();
86
- }
87
-
88
- /**
89
- * Setup axios interceptors for authentication and error handling
90
- */
91
- setupInterceptors() {
92
- // Request interceptor: Add authentication header
93
- this.client.interceptors.request.use(async req => {
94
- const accessToken = this.tokenStore.getAccessToken();
95
- if (!accessToken) {
96
- return req;
97
- }
98
- try {
99
- const decoded = (0, _jwtDecode.jwtDecode)(accessToken);
100
- const currentTime = Math.floor(Date.now() / 1000);
101
-
102
- // If token expires in less than 60 seconds, refresh it
103
- if (decoded.exp && decoded.exp - currentTime < 60) {
104
- if (decoded.sessionId) {
105
- try {
106
- // Create a new axios instance to avoid interceptor recursion
107
- const refreshClient = _axios.default.create({
108
- baseURL: this.client.defaults.baseURL,
109
- timeout: this.client.defaults.timeout
110
- });
111
- const res = await refreshClient.get(`/api/session/token/${decoded.sessionId}`);
112
- this.tokenStore.setTokens(res.data.accessToken);
113
- req.headers.Authorization = `Bearer ${res.data.accessToken}`;
114
- this.logger.debug('Token refreshed');
115
- } catch (refreshError) {
116
- // If refresh fails, use current token anyway
117
- req.headers.Authorization = `Bearer ${accessToken}`;
118
- this.logger.warn('Token refresh failed, using current token');
119
- }
120
- } else {
121
- req.headers.Authorization = `Bearer ${accessToken}`;
122
- }
123
- } else {
124
- req.headers.Authorization = `Bearer ${accessToken}`;
125
- }
126
- } catch (error) {
127
- this.logger.error('Error processing token:', error);
128
- // Even if there's an error, still try to use the token
129
- req.headers.Authorization = `Bearer ${accessToken}`;
130
- }
131
- return req;
132
- }, error => {
133
- this.logger.error('Request interceptor error:', error);
134
- return Promise.reject(error);
135
- });
136
-
137
- // Response interceptor: Handle auth errors
138
- this.client.interceptors.response.use(response => response, error => {
139
- if (error.response?.status === 401) {
140
- this.logger.warn('401 Unauthorized, clearing tokens');
141
- this.tokenStore.clearTokens();
142
- }
143
- return Promise.reject(error);
144
- });
145
- }
146
-
147
- /**
148
- * Get the underlying Axios instance
149
- * Use this only when you need direct access to Axios features
150
- */
151
- getAxiosInstance() {
152
- return this.client;
153
- }
154
-
155
- /**
156
- * Make a raw HTTP request (no caching, deduplication, etc.)
157
- * Use this for requests that need to bypass performance features
158
- */
159
- async request(config) {
160
- try {
161
- const response = await this.client.request({
162
- method: config.method,
163
- url: config.url,
164
- data: config.data,
165
- params: config.params,
166
- timeout: config.timeout,
167
- signal: config.signal
168
- });
169
-
170
- // Unwrap standardized API response format: { data: ... }
171
- // This handles responses from sendSuccess() and sendPaginated() helpers
172
- const responseData = response.data;
173
-
174
- // Handle paginated responses: { data: [...], pagination: {...} }
175
- // Return the data array directly - the calling method will wrap it appropriately
176
- if (responseData && typeof responseData === 'object' && 'data' in responseData && 'pagination' in responseData) {
177
- // For paginated responses, return the data array directly
178
- // The calling methods like getUserFollowers/getUserFollowing will handle wrapping
179
- // We return the whole response so methods can access both data and pagination
180
- return responseData;
181
- }
182
-
183
- // Handle regular success responses: { data: ... }
184
- if (responseData && typeof responseData === 'object' && 'data' in responseData && !Array.isArray(responseData)) {
185
- return responseData.data;
186
- }
187
-
188
- // Return as-is for responses that don't use sendSuccess wrapper
189
- return responseData;
190
- } catch (error) {
191
- throw (0, _errorUtils.handleHttpError)(error);
192
- }
193
- }
194
-
195
- /**
196
- * GET request convenience method
197
- */
198
- async get(url, config) {
199
- const response = await this.request({
200
- method: 'GET',
201
- url,
202
- params: config?.params,
203
- timeout: config?.timeout,
204
- signal: config?.signal
205
- });
206
- return {
207
- data: response
208
- };
209
- }
210
-
211
- /**
212
- * POST request convenience method
213
- */
214
- async post(url, data, config) {
215
- const response = await this.request({
216
- method: 'POST',
217
- url,
218
- data,
219
- timeout: config?.timeout,
220
- signal: config?.signal
221
- });
222
- return {
223
- data: response
224
- };
225
- }
226
-
227
- /**
228
- * PUT request convenience method
229
- */
230
- async put(url, data, config) {
231
- const response = await this.request({
232
- method: 'PUT',
233
- url,
234
- data,
235
- timeout: config?.timeout,
236
- signal: config?.signal
237
- });
238
- return {
239
- data: response
240
- };
241
- }
242
-
243
- /**
244
- * PATCH request convenience method
245
- */
246
- async patch(url, data, config) {
247
- const response = await this.request({
248
- method: 'PATCH',
249
- url,
250
- data,
251
- timeout: config?.timeout,
252
- signal: config?.signal
253
- });
254
- return {
255
- data: response
256
- };
257
- }
258
-
259
- /**
260
- * DELETE request convenience method
261
- */
262
- async delete(url, config) {
263
- const response = await this.request({
264
- method: 'DELETE',
265
- url,
266
- timeout: config?.timeout,
267
- signal: config?.signal
268
- });
269
- return {
270
- data: response
271
- };
272
- }
273
-
274
- /**
275
- * Get base URL
276
- */
277
- getBaseURL() {
278
- return this.baseURL;
279
- }
280
-
281
- /**
282
- * Set authentication tokens
283
- */
284
- setTokens(accessToken, refreshToken = '') {
285
- this.tokenStore.setTokens(accessToken, refreshToken);
286
- }
287
-
288
- /**
289
- * Clear authentication tokens
290
- */
291
- clearTokens() {
292
- this.tokenStore.clearTokens();
293
- }
294
-
295
- /**
296
- * Get access token
297
- */
298
- getAccessToken() {
299
- return this.tokenStore.getAccessToken();
300
- }
301
-
302
- /**
303
- * Check if has access token
304
- */
305
- hasAccessToken() {
306
- return this.tokenStore.hasAccessToken();
307
- }
308
-
309
- // Test-only utility to reset global tokens between jest tests
310
- static __resetTokensForTests() {
311
- try {
312
- TokenStore.getInstance().clearTokens();
313
- } catch {}
314
- }
315
- }
316
- exports.HttpClient = HttpClient;
317
- //# sourceMappingURL=HttpClient.js.map