@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.
- package/lib/commonjs/core/HttpService.js +481 -0
- package/lib/commonjs/core/HttpService.js.map +1 -0
- package/lib/commonjs/core/OxyServices.base.js +29 -26
- package/lib/commonjs/core/OxyServices.base.js.map +1 -1
- package/lib/commonjs/core/OxyServices.js +1 -2
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.assets.js +3 -2
- package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.user.js +9 -5
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.utility.js +1 -0
- package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -1
- package/lib/commonjs/utils/errorUtils.js +35 -15
- package/lib/commonjs/utils/errorUtils.js.map +1 -1
- package/lib/module/core/HttpService.js +476 -0
- package/lib/module/core/HttpService.js.map +1 -0
- package/lib/module/core/OxyServices.base.js +29 -26
- package/lib/module/core/OxyServices.base.js.map +1 -1
- package/lib/module/core/OxyServices.js +1 -2
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.assets.js +3 -2
- package/lib/module/core/mixins/OxyServices.assets.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.user.js +9 -5
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.utility.js +1 -0
- package/lib/module/core/mixins/OxyServices.utility.js.map +1 -1
- package/lib/module/utils/errorUtils.js +36 -15
- package/lib/module/utils/errorUtils.js.map +1 -1
- package/lib/typescript/core/HttpService.d.ts +111 -0
- package/lib/typescript/core/HttpService.d.ts.map +1 -0
- package/lib/typescript/core/OxyServices.base.d.ts +6 -8
- package/lib/typescript/core/OxyServices.base.d.ts.map +1 -1
- package/lib/typescript/core/OxyServices.d.ts +1 -2
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.analytics.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.assets.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.developer.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.devices.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.karma.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.language.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.location.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.payment.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.privacy.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.totp.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.totp.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.utility.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -1
- package/lib/typescript/core/mixins/index.d.ts +52 -65
- package/lib/typescript/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/utils/errorUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/HttpService.ts +523 -0
- package/src/core/OxyServices.base.ts +36 -34
- package/src/core/OxyServices.ts +1 -2
- package/src/core/mixins/OxyServices.assets.ts +2 -1
- package/src/core/mixins/OxyServices.user.ts +7 -6
- package/src/core/mixins/OxyServices.utility.ts +1 -0
- package/src/utils/errorUtils.ts +65 -19
- package/lib/commonjs/core/HttpClient.js +0 -317
- package/lib/commonjs/core/HttpClient.js.map +0 -1
- package/lib/commonjs/core/RequestManager.js +0 -170
- package/lib/commonjs/core/RequestManager.js.map +0 -1
- package/lib/module/core/HttpClient.js +0 -311
- package/lib/module/core/HttpClient.js.map +0 -1
- package/lib/module/core/RequestManager.js +0 -165
- package/lib/module/core/RequestManager.js.map +0 -1
- package/lib/typescript/core/HttpClient.d.ts +0 -110
- package/lib/typescript/core/HttpClient.d.ts.map +0 -1
- package/lib/typescript/core/RequestManager.d.ts +0 -63
- package/lib/typescript/core/RequestManager.d.ts.map +0 -1
- package/src/core/HttpClient.ts +0 -346
- package/src/core/RequestManager.ts +0 -205
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unified HTTP Service
|
|
5
|
+
*
|
|
6
|
+
* Consolidates HttpClient + RequestManager into a single efficient class.
|
|
7
|
+
* Uses native fetch instead of axios for smaller bundle size.
|
|
8
|
+
*
|
|
9
|
+
* Handles:
|
|
10
|
+
* - Authentication (token management, auto-refresh)
|
|
11
|
+
* - Caching (TTL-based)
|
|
12
|
+
* - Deduplication (concurrent requests)
|
|
13
|
+
* - Retry logic
|
|
14
|
+
* - Error handling
|
|
15
|
+
* - Request queuing
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { TTLCache, registerCacheForCleanup } from '../utils/cache';
|
|
19
|
+
import { RequestDeduplicator, RequestQueue, SimpleLogger } from '../utils/requestUtils';
|
|
20
|
+
import { retryAsync } from '../utils/asyncUtils';
|
|
21
|
+
import { handleHttpError } from '../utils/errorUtils';
|
|
22
|
+
import { jwtDecode } from 'jwt-decode';
|
|
23
|
+
/**
|
|
24
|
+
* Token store for authentication (singleton)
|
|
25
|
+
*/
|
|
26
|
+
class TokenStore {
|
|
27
|
+
accessToken = null;
|
|
28
|
+
refreshToken = null;
|
|
29
|
+
constructor() {}
|
|
30
|
+
static getInstance() {
|
|
31
|
+
if (!TokenStore.instance) {
|
|
32
|
+
TokenStore.instance = new TokenStore();
|
|
33
|
+
}
|
|
34
|
+
return TokenStore.instance;
|
|
35
|
+
}
|
|
36
|
+
setTokens(accessToken, refreshToken = '') {
|
|
37
|
+
this.accessToken = accessToken;
|
|
38
|
+
this.refreshToken = refreshToken;
|
|
39
|
+
}
|
|
40
|
+
getAccessToken() {
|
|
41
|
+
return this.accessToken;
|
|
42
|
+
}
|
|
43
|
+
getRefreshToken() {
|
|
44
|
+
return this.refreshToken;
|
|
45
|
+
}
|
|
46
|
+
clearTokens() {
|
|
47
|
+
this.accessToken = null;
|
|
48
|
+
this.refreshToken = null;
|
|
49
|
+
}
|
|
50
|
+
hasAccessToken() {
|
|
51
|
+
return !!this.accessToken;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Unified HTTP Service
|
|
57
|
+
*
|
|
58
|
+
* Consolidates HttpClient + RequestManager into a single efficient class.
|
|
59
|
+
* Uses native fetch instead of axios for smaller bundle size.
|
|
60
|
+
*/
|
|
61
|
+
export class HttpService {
|
|
62
|
+
// Performance monitoring
|
|
63
|
+
requestMetrics = {
|
|
64
|
+
totalRequests: 0,
|
|
65
|
+
successfulRequests: 0,
|
|
66
|
+
failedRequests: 0,
|
|
67
|
+
cacheHits: 0,
|
|
68
|
+
cacheMisses: 0,
|
|
69
|
+
averageResponseTime: 0
|
|
70
|
+
};
|
|
71
|
+
constructor(config) {
|
|
72
|
+
this.config = config;
|
|
73
|
+
this.baseURL = config.baseURL;
|
|
74
|
+
this.tokenStore = TokenStore.getInstance();
|
|
75
|
+
this.logger = new SimpleLogger(config.enableLogging || false, config.logLevel || 'error', 'HttpService');
|
|
76
|
+
|
|
77
|
+
// Initialize performance infrastructure
|
|
78
|
+
this.cache = new TTLCache(config.cacheTTL || 5 * 60 * 1000);
|
|
79
|
+
registerCacheForCleanup(this.cache);
|
|
80
|
+
this.deduplicator = new RequestDeduplicator();
|
|
81
|
+
this.requestQueue = new RequestQueue(config.maxConcurrentRequests || 10, config.requestQueueSize || 100);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Main request method - handles everything in one place
|
|
86
|
+
*/
|
|
87
|
+
async request(config) {
|
|
88
|
+
const {
|
|
89
|
+
method,
|
|
90
|
+
url,
|
|
91
|
+
data,
|
|
92
|
+
params,
|
|
93
|
+
timeout = this.config.requestTimeout || 5000,
|
|
94
|
+
signal,
|
|
95
|
+
cache = method === 'GET',
|
|
96
|
+
cacheTTL,
|
|
97
|
+
deduplicate = true,
|
|
98
|
+
retry = this.config.enableRetry !== false,
|
|
99
|
+
maxRetries = this.config.maxRetries || 3
|
|
100
|
+
} = config;
|
|
101
|
+
|
|
102
|
+
// Generate cache key (optimized for large objects)
|
|
103
|
+
const cacheKey = cache ? this.generateCacheKey(method, url, data || params) : null;
|
|
104
|
+
|
|
105
|
+
// Check cache first
|
|
106
|
+
if (cache && cacheKey) {
|
|
107
|
+
const cached = this.cache.get(cacheKey);
|
|
108
|
+
if (cached !== null) {
|
|
109
|
+
this.requestMetrics.cacheHits++;
|
|
110
|
+
this.logger.debug('Cache hit:', url);
|
|
111
|
+
return cached;
|
|
112
|
+
}
|
|
113
|
+
this.requestMetrics.cacheMisses++;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Request function
|
|
117
|
+
const requestFn = async () => {
|
|
118
|
+
const startTime = Date.now();
|
|
119
|
+
try {
|
|
120
|
+
// Build URL with params
|
|
121
|
+
const fullUrl = this.buildURL(url, params);
|
|
122
|
+
|
|
123
|
+
// Get auth token (with auto-refresh)
|
|
124
|
+
const authHeader = await this.getAuthHeader();
|
|
125
|
+
|
|
126
|
+
// Determine if data is FormData
|
|
127
|
+
const isFormData = data instanceof FormData;
|
|
128
|
+
|
|
129
|
+
// Make fetch request
|
|
130
|
+
const controller = new AbortController();
|
|
131
|
+
const timeoutId = timeout ? setTimeout(() => controller.abort(), timeout) : null;
|
|
132
|
+
if (signal) {
|
|
133
|
+
signal.addEventListener('abort', () => controller.abort());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Build headers
|
|
137
|
+
const headers = {
|
|
138
|
+
'Accept': 'application/json'
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Only set Content-Type for non-FormData requests (FormData sets it automatically with boundary)
|
|
142
|
+
if (!isFormData) {
|
|
143
|
+
headers['Content-Type'] = 'application/json';
|
|
144
|
+
}
|
|
145
|
+
if (authHeader) {
|
|
146
|
+
headers['Authorization'] = authHeader;
|
|
147
|
+
}
|
|
148
|
+
const response = await fetch(fullUrl, {
|
|
149
|
+
method,
|
|
150
|
+
headers,
|
|
151
|
+
body: method !== 'GET' && data ? isFormData ? data : JSON.stringify(data) : undefined,
|
|
152
|
+
signal: controller.signal
|
|
153
|
+
});
|
|
154
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
155
|
+
|
|
156
|
+
// Handle response
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
if (response.status === 401) {
|
|
159
|
+
this.tokenStore.clearTokens();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Try to parse error response (handle empty/malformed JSON)
|
|
163
|
+
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
|
164
|
+
const contentType = response.headers.get('content-type');
|
|
165
|
+
if (contentType && contentType.includes('application/json')) {
|
|
166
|
+
try {
|
|
167
|
+
const errorData = await response.json();
|
|
168
|
+
if (errorData?.message) {
|
|
169
|
+
errorMessage = errorData.message;
|
|
170
|
+
}
|
|
171
|
+
} catch (parseError) {
|
|
172
|
+
// Malformed JSON or empty response - use status text
|
|
173
|
+
this.logger.warn('Failed to parse error response JSON:', parseError);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const error = new Error(errorMessage);
|
|
177
|
+
error.status = response.status;
|
|
178
|
+
error.response = {
|
|
179
|
+
status: response.status,
|
|
180
|
+
statusText: response.statusText
|
|
181
|
+
};
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Handle different response types (optimized - read response once)
|
|
186
|
+
const contentType = response.headers.get('content-type');
|
|
187
|
+
let responseData;
|
|
188
|
+
if (contentType && contentType.includes('application/json')) {
|
|
189
|
+
// Use response.json() directly for better performance
|
|
190
|
+
try {
|
|
191
|
+
responseData = await response.json();
|
|
192
|
+
// Handle null/undefined responses
|
|
193
|
+
if (responseData === null || responseData === undefined) {
|
|
194
|
+
responseData = null;
|
|
195
|
+
} else {
|
|
196
|
+
// Unwrap standardized API response format for JSON
|
|
197
|
+
responseData = this.unwrapResponse(responseData);
|
|
198
|
+
}
|
|
199
|
+
} catch (parseError) {
|
|
200
|
+
// Handle malformed JSON or empty responses gracefully
|
|
201
|
+
// Note: Once response.json() is called, the body is consumed and cannot be read again
|
|
202
|
+
// So we check the error type to determine if it's empty or malformed
|
|
203
|
+
if (parseError instanceof SyntaxError) {
|
|
204
|
+
this.logger.warn('Failed to parse JSON response (malformed or empty):', parseError);
|
|
205
|
+
// SyntaxError typically means empty or malformed JSON
|
|
206
|
+
// For empty responses, return null; for malformed JSON, throw descriptive error
|
|
207
|
+
responseData = null; // Treat as empty response for safety
|
|
208
|
+
} else {
|
|
209
|
+
this.logger.warn('Failed to read response:', parseError);
|
|
210
|
+
throw new Error('Failed to read response from server');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} else if (contentType && (contentType.includes('application/octet-stream') || contentType.includes('image/') || contentType.includes('video/') || contentType.includes('audio/'))) {
|
|
214
|
+
// For binary responses (blobs), return the blob directly without unwrapping
|
|
215
|
+
responseData = await response.blob();
|
|
216
|
+
} else {
|
|
217
|
+
// For other responses, return as text
|
|
218
|
+
const text = await response.text();
|
|
219
|
+
responseData = text || null;
|
|
220
|
+
}
|
|
221
|
+
const duration = Date.now() - startTime;
|
|
222
|
+
this.updateMetrics(true, duration);
|
|
223
|
+
this.config.onRequestEnd?.(url, method, duration, true);
|
|
224
|
+
return responseData;
|
|
225
|
+
} catch (error) {
|
|
226
|
+
const duration = Date.now() - startTime;
|
|
227
|
+
this.updateMetrics(false, duration);
|
|
228
|
+
this.config.onRequestEnd?.(url, method, duration, false);
|
|
229
|
+
this.config.onRequestError?.(url, method, error instanceof Error ? error : new Error(String(error)));
|
|
230
|
+
|
|
231
|
+
// Handle AbortError specifically for better error messages
|
|
232
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
233
|
+
throw handleHttpError(error);
|
|
234
|
+
}
|
|
235
|
+
throw handleHttpError(error);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Wrap with retry if enabled
|
|
240
|
+
const requestWithRetry = retry ? () => retryAsync(requestFn, maxRetries, this.config.retryDelay || 1000) : requestFn;
|
|
241
|
+
|
|
242
|
+
// Wrap with deduplication if enabled (use optimized key generation)
|
|
243
|
+
const dedupeKey = deduplicate ? this.generateCacheKey(method, url, data || params) : null;
|
|
244
|
+
const finalRequest = dedupeKey ? () => this.deduplicator.deduplicate(dedupeKey, requestWithRetry) : requestWithRetry;
|
|
245
|
+
|
|
246
|
+
// Execute request (with queue if needed)
|
|
247
|
+
const result = await this.requestQueue.enqueue(finalRequest);
|
|
248
|
+
|
|
249
|
+
// Cache the result if caching is enabled
|
|
250
|
+
if (cache && cacheKey && result) {
|
|
251
|
+
this.cache.set(cacheKey, result, cacheTTL);
|
|
252
|
+
}
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Generate cache key efficiently
|
|
258
|
+
* Uses simple hash for large objects to avoid expensive JSON.stringify
|
|
259
|
+
*/
|
|
260
|
+
generateCacheKey(method, url, data) {
|
|
261
|
+
if (!data || typeof data === 'object' && Object.keys(data).length === 0) {
|
|
262
|
+
return `${method}:${url}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// For small objects, use JSON.stringify
|
|
266
|
+
const dataStr = JSON.stringify(data);
|
|
267
|
+
if (dataStr.length < 1000) {
|
|
268
|
+
return `${method}:${url}:${dataStr}`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// For large objects, use a simple hash based on keys and values length
|
|
272
|
+
// This avoids expensive serialization while still being unique enough
|
|
273
|
+
const hash = typeof data === 'object' && data !== null ? Object.keys(data).sort().join(',') + ':' + dataStr.length : String(data).substring(0, 100);
|
|
274
|
+
return `${method}:${url}:${hash}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Build full URL with query params
|
|
279
|
+
*/
|
|
280
|
+
buildURL(url, params) {
|
|
281
|
+
const base = url.startsWith('http') ? url : `${this.baseURL}${url}`;
|
|
282
|
+
if (!params || Object.keys(params).length === 0) {
|
|
283
|
+
return base;
|
|
284
|
+
}
|
|
285
|
+
const searchParams = new URLSearchParams();
|
|
286
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
287
|
+
if (value !== undefined && value !== null) {
|
|
288
|
+
searchParams.append(key, String(value));
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
const queryString = searchParams.toString();
|
|
292
|
+
return queryString ? `${base}${base.includes('?') ? '&' : '?'}${queryString}` : base;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Get auth header with automatic token refresh
|
|
297
|
+
*/
|
|
298
|
+
async getAuthHeader() {
|
|
299
|
+
const accessToken = this.tokenStore.getAccessToken();
|
|
300
|
+
if (!accessToken) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
const decoded = jwtDecode(accessToken);
|
|
305
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
306
|
+
|
|
307
|
+
// If token expires in less than 60 seconds, refresh it
|
|
308
|
+
if (decoded.exp && decoded.exp - currentTime < 60 && decoded.sessionId) {
|
|
309
|
+
try {
|
|
310
|
+
const refreshUrl = `${this.baseURL}/api/session/token/${decoded.sessionId}`;
|
|
311
|
+
|
|
312
|
+
// Use AbortSignal.timeout for consistent timeout handling
|
|
313
|
+
const response = await fetch(refreshUrl, {
|
|
314
|
+
method: 'GET',
|
|
315
|
+
headers: {
|
|
316
|
+
'Accept': 'application/json'
|
|
317
|
+
},
|
|
318
|
+
signal: AbortSignal.timeout(5000)
|
|
319
|
+
});
|
|
320
|
+
if (response.ok) {
|
|
321
|
+
const {
|
|
322
|
+
accessToken: newToken
|
|
323
|
+
} = await response.json();
|
|
324
|
+
this.tokenStore.setTokens(newToken);
|
|
325
|
+
this.logger.debug('Token refreshed');
|
|
326
|
+
return `Bearer ${newToken}`;
|
|
327
|
+
}
|
|
328
|
+
} catch (refreshError) {
|
|
329
|
+
this.logger.warn('Token refresh failed, using current token');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return `Bearer ${accessToken}`;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
this.logger.error('Error processing token:', error);
|
|
335
|
+
return `Bearer ${accessToken}`;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Unwrap standardized API response format
|
|
341
|
+
*/
|
|
342
|
+
unwrapResponse(responseData) {
|
|
343
|
+
// Handle paginated responses: { data: [...], pagination: {...} }
|
|
344
|
+
if (responseData && typeof responseData === 'object' && 'data' in responseData && 'pagination' in responseData) {
|
|
345
|
+
return responseData;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Handle regular success responses: { data: ... }
|
|
349
|
+
if (responseData && typeof responseData === 'object' && 'data' in responseData && !Array.isArray(responseData)) {
|
|
350
|
+
return responseData.data;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Return as-is for responses that don't use sendSuccess wrapper
|
|
354
|
+
return responseData;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Update request metrics
|
|
359
|
+
*/
|
|
360
|
+
updateMetrics(success, duration) {
|
|
361
|
+
this.requestMetrics.totalRequests++;
|
|
362
|
+
if (success) {
|
|
363
|
+
this.requestMetrics.successfulRequests++;
|
|
364
|
+
} else {
|
|
365
|
+
this.requestMetrics.failedRequests++;
|
|
366
|
+
}
|
|
367
|
+
const alpha = 0.1;
|
|
368
|
+
this.requestMetrics.averageResponseTime = this.requestMetrics.averageResponseTime * (1 - alpha) + duration * alpha;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Convenience methods (for backward compatibility)
|
|
372
|
+
async get(url, config) {
|
|
373
|
+
const result = await this.request({
|
|
374
|
+
method: 'GET',
|
|
375
|
+
url,
|
|
376
|
+
...config
|
|
377
|
+
});
|
|
378
|
+
return {
|
|
379
|
+
data: result
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
async post(url, data, config) {
|
|
383
|
+
const result = await this.request({
|
|
384
|
+
method: 'POST',
|
|
385
|
+
url,
|
|
386
|
+
data,
|
|
387
|
+
...config
|
|
388
|
+
});
|
|
389
|
+
return {
|
|
390
|
+
data: result
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
async put(url, data, config) {
|
|
394
|
+
const result = await this.request({
|
|
395
|
+
method: 'PUT',
|
|
396
|
+
url,
|
|
397
|
+
data,
|
|
398
|
+
...config
|
|
399
|
+
});
|
|
400
|
+
return {
|
|
401
|
+
data: result
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
async patch(url, data, config) {
|
|
405
|
+
const result = await this.request({
|
|
406
|
+
method: 'PATCH',
|
|
407
|
+
url,
|
|
408
|
+
data,
|
|
409
|
+
...config
|
|
410
|
+
});
|
|
411
|
+
return {
|
|
412
|
+
data: result
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
async delete(url, config) {
|
|
416
|
+
const result = await this.request({
|
|
417
|
+
method: 'DELETE',
|
|
418
|
+
url,
|
|
419
|
+
...config
|
|
420
|
+
});
|
|
421
|
+
return {
|
|
422
|
+
data: result
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Token management
|
|
427
|
+
setTokens(accessToken, refreshToken = '') {
|
|
428
|
+
this.tokenStore.setTokens(accessToken, refreshToken);
|
|
429
|
+
}
|
|
430
|
+
clearTokens() {
|
|
431
|
+
this.tokenStore.clearTokens();
|
|
432
|
+
}
|
|
433
|
+
getAccessToken() {
|
|
434
|
+
return this.tokenStore.getAccessToken();
|
|
435
|
+
}
|
|
436
|
+
hasAccessToken() {
|
|
437
|
+
return this.tokenStore.hasAccessToken();
|
|
438
|
+
}
|
|
439
|
+
getBaseURL() {
|
|
440
|
+
return this.baseURL;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Cache management
|
|
444
|
+
clearCache() {
|
|
445
|
+
this.cache.clear();
|
|
446
|
+
}
|
|
447
|
+
clearCacheEntry(key) {
|
|
448
|
+
this.cache.delete(key);
|
|
449
|
+
}
|
|
450
|
+
getCacheStats() {
|
|
451
|
+
const cacheStats = this.cache.getStats();
|
|
452
|
+
const total = this.requestMetrics.cacheHits + this.requestMetrics.cacheMisses;
|
|
453
|
+
return {
|
|
454
|
+
size: cacheStats.size,
|
|
455
|
+
hits: this.requestMetrics.cacheHits,
|
|
456
|
+
misses: this.requestMetrics.cacheMisses,
|
|
457
|
+
hitRate: total > 0 ? this.requestMetrics.cacheHits / total : 0
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
getMetrics() {
|
|
461
|
+
return {
|
|
462
|
+
...this.requestMetrics
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Test-only utility
|
|
467
|
+
static __resetTokensForTests() {
|
|
468
|
+
try {
|
|
469
|
+
TokenStore.getInstance().clearTokens();
|
|
470
|
+
} catch (error) {
|
|
471
|
+
// Silently fail in test cleanup - this is expected behavior
|
|
472
|
+
// TokenStore might not be initialized in some test scenarios
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
//# sourceMappingURL=HttpService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TTLCache","registerCacheForCleanup","RequestDeduplicator","RequestQueue","SimpleLogger","retryAsync","handleHttpError","jwtDecode","TokenStore","accessToken","refreshToken","constructor","getInstance","instance","setTokens","getAccessToken","getRefreshToken","clearTokens","hasAccessToken","HttpService","requestMetrics","totalRequests","successfulRequests","failedRequests","cacheHits","cacheMisses","averageResponseTime","config","baseURL","tokenStore","logger","enableLogging","logLevel","cache","cacheTTL","deduplicator","requestQueue","maxConcurrentRequests","requestQueueSize","request","method","url","data","params","timeout","requestTimeout","signal","deduplicate","retry","enableRetry","maxRetries","cacheKey","generateCacheKey","cached","get","debug","requestFn","startTime","Date","now","fullUrl","buildURL","authHeader","getAuthHeader","isFormData","FormData","controller","AbortController","timeoutId","setTimeout","abort","addEventListener","headers","response","fetch","body","JSON","stringify","undefined","clearTimeout","ok","status","errorMessage","statusText","contentType","includes","errorData","json","message","parseError","warn","error","Error","responseData","unwrapResponse","SyntaxError","blob","text","duration","updateMetrics","onRequestEnd","onRequestError","String","name","requestWithRetry","retryDelay","dedupeKey","finalRequest","result","enqueue","set","Object","keys","length","dataStr","hash","sort","join","substring","base","startsWith","searchParams","URLSearchParams","entries","forEach","key","value","append","queryString","toString","decoded","currentTime","Math","floor","exp","sessionId","refreshUrl","AbortSignal","newToken","refreshError","Array","isArray","success","alpha","post","put","patch","delete","getBaseURL","clearCache","clear","clearCacheEntry","getCacheStats","cacheStats","getStats","total","size","hits","misses","hitRate","getMetrics","__resetTokensForTests"],"sourceRoot":"../../../src","sources":["core/HttpService.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { jwtDecode } from 'jwt-decode';
|
|
9
9
|
import { handleHttpError } from '../utils/errorUtils';
|
|
10
|
-
import {
|
|
11
|
-
import { RequestManager } from './RequestManager';
|
|
10
|
+
import { HttpService } from './HttpService';
|
|
12
11
|
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors';
|
|
13
12
|
/**
|
|
14
13
|
* Base class for OxyServices with core infrastructure
|
|
@@ -22,16 +21,13 @@ export class OxyServicesBase {
|
|
|
22
21
|
this.config = config;
|
|
23
22
|
this.cloudURL = config.cloudURL || 'https://cloud.oxy.so';
|
|
24
23
|
|
|
25
|
-
// Initialize HTTP
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
// Initialize request manager (handles caching, deduplication, queuing, retry)
|
|
29
|
-
this.requestManager = new RequestManager(this.httpClient, config);
|
|
24
|
+
// Initialize unified HTTP service (handles auth, caching, deduplication, queuing, retry)
|
|
25
|
+
this.httpService = new HttpService(config);
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
// Test-only utility to reset global tokens between jest tests
|
|
33
29
|
static __resetTokensForTests() {
|
|
34
|
-
|
|
30
|
+
HttpService.__resetTokensForTests();
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
/**
|
|
@@ -39,7 +35,13 @@ export class OxyServicesBase {
|
|
|
39
35
|
* This is the main method for all API calls - ensures authentication and performance features
|
|
40
36
|
*/
|
|
41
37
|
async makeRequest(method, url, data, options = {}) {
|
|
42
|
-
return this.
|
|
38
|
+
return this.httpService.request({
|
|
39
|
+
method,
|
|
40
|
+
url,
|
|
41
|
+
data: method !== 'GET' ? data : undefined,
|
|
42
|
+
params: method === 'GET' ? data : undefined,
|
|
43
|
+
...options
|
|
44
|
+
});
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
// ============================================================================
|
|
@@ -50,43 +52,43 @@ export class OxyServicesBase {
|
|
|
50
52
|
* Get the configured Oxy API base URL
|
|
51
53
|
*/
|
|
52
54
|
getBaseURL() {
|
|
53
|
-
return this.
|
|
55
|
+
return this.httpService.getBaseURL();
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
|
-
* Get the HTTP
|
|
58
|
-
* Useful for advanced use cases where direct access to the HTTP
|
|
59
|
+
* Get the HTTP service instance
|
|
60
|
+
* Useful for advanced use cases where direct access to the HTTP service is needed
|
|
59
61
|
*/
|
|
60
62
|
getClient() {
|
|
61
|
-
return this.
|
|
63
|
+
return this.httpService;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
/**
|
|
65
67
|
* Get performance metrics
|
|
66
68
|
*/
|
|
67
69
|
getMetrics() {
|
|
68
|
-
return this.
|
|
70
|
+
return this.httpService.getMetrics();
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
74
|
* Clear request cache
|
|
73
75
|
*/
|
|
74
76
|
clearCache() {
|
|
75
|
-
this.
|
|
77
|
+
this.httpService.clearCache();
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
/**
|
|
79
81
|
* Clear specific cache entry
|
|
80
82
|
*/
|
|
81
83
|
clearCacheEntry(key) {
|
|
82
|
-
this.
|
|
84
|
+
this.httpService.clearCacheEntry(key);
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
/**
|
|
86
88
|
* Get cache statistics
|
|
87
89
|
*/
|
|
88
90
|
getCacheStats() {
|
|
89
|
-
return this.
|
|
91
|
+
return this.httpService.getCacheStats();
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
/**
|
|
@@ -100,21 +102,21 @@ export class OxyServicesBase {
|
|
|
100
102
|
* Set authentication tokens
|
|
101
103
|
*/
|
|
102
104
|
setTokens(accessToken, refreshToken = '') {
|
|
103
|
-
this.
|
|
105
|
+
this.httpService.setTokens(accessToken, refreshToken);
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
/**
|
|
107
109
|
* Clear stored authentication tokens
|
|
108
110
|
*/
|
|
109
111
|
clearTokens() {
|
|
110
|
-
this.
|
|
112
|
+
this.httpService.clearTokens();
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
/**
|
|
114
116
|
* Get the current user ID from the access token
|
|
115
117
|
*/
|
|
116
118
|
getCurrentUserId() {
|
|
117
|
-
const accessToken = this.
|
|
119
|
+
const accessToken = this.httpService.getAccessToken();
|
|
118
120
|
if (!accessToken) {
|
|
119
121
|
return null;
|
|
120
122
|
}
|
|
@@ -130,14 +132,14 @@ export class OxyServicesBase {
|
|
|
130
132
|
* Check if the client has a valid access token (public method)
|
|
131
133
|
*/
|
|
132
134
|
hasValidToken() {
|
|
133
|
-
return this.
|
|
135
|
+
return this.httpService.hasAccessToken();
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
/**
|
|
137
139
|
* Get the raw access token (for constructing anchor URLs when needed)
|
|
138
140
|
*/
|
|
139
141
|
getAccessToken() {
|
|
140
|
-
return this.
|
|
142
|
+
return this.httpService.getAccessToken();
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
/**
|
|
@@ -160,7 +162,7 @@ export class OxyServicesBase {
|
|
|
160
162
|
*/
|
|
161
163
|
async waitForAuth(timeoutMs = 5000) {
|
|
162
164
|
// Immediate synchronous check - no delay if token is ready
|
|
163
|
-
if (this.
|
|
165
|
+
if (this.httpService.hasAccessToken()) {
|
|
164
166
|
return true;
|
|
165
167
|
}
|
|
166
168
|
const startTime = performance.now();
|
|
@@ -171,7 +173,7 @@ export class OxyServicesBase {
|
|
|
171
173
|
|
|
172
174
|
while (performance.now() < maxTime) {
|
|
173
175
|
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
174
|
-
if (this.
|
|
176
|
+
if (this.httpService.hasAccessToken()) {
|
|
175
177
|
return true;
|
|
176
178
|
}
|
|
177
179
|
|
|
@@ -197,7 +199,7 @@ export class OxyServicesBase {
|
|
|
197
199
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
198
200
|
try {
|
|
199
201
|
// First attempt: check if we have a token
|
|
200
|
-
if (!this.
|
|
202
|
+
if (!this.httpService.hasAccessToken()) {
|
|
201
203
|
if (attempt === 0) {
|
|
202
204
|
// On first attempt, wait briefly for authentication to complete
|
|
203
205
|
const authReady = await this.waitForAuth(authTimeoutMs);
|
|
@@ -214,7 +216,8 @@ export class OxyServicesBase {
|
|
|
214
216
|
return await operation();
|
|
215
217
|
} catch (error) {
|
|
216
218
|
const isLastAttempt = attempt === maxRetries;
|
|
217
|
-
const
|
|
219
|
+
const errorObj = error && typeof error === 'object' ? error : null;
|
|
220
|
+
const isAuthError = errorObj?.response?.status === 401 || errorObj?.code === 'MISSING_TOKEN' || errorObj?.message?.includes('Authentication') || error instanceof OxyAuthenticationError;
|
|
218
221
|
if (isAuthError && !isLastAttempt && !(error instanceof OxyAuthenticationTimeoutError)) {
|
|
219
222
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
220
223
|
continue;
|