@oxyhq/services 5.13.25 → 5.13.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -6
- 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 +3 -4
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.assets.js +3 -9
- 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/index.js +15 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileCard.js +5 -1
- package/lib/commonjs/ui/components/ProfileCard.js.map +1 -1
- package/lib/commonjs/ui/hooks/index.js +13 -0
- package/lib/commonjs/ui/hooks/index.js.map +1 -1
- package/lib/commonjs/ui/hooks/useFileDownloadUrl.js +103 -0
- package/lib/commonjs/ui/hooks/useFileDownloadUrl.js.map +1 -0
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js +0 -3
- package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.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 +3 -4
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.assets.js +3 -9
- 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/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/ProfileCard.js +5 -1
- package/lib/module/ui/components/ProfileCard.js.map +1 -1
- package/lib/module/ui/hooks/index.js +1 -0
- package/lib/module/ui/hooks/index.js.map +1 -1
- package/lib/module/ui/hooks/useFileDownloadUrl.js +97 -0
- package/lib/module/ui/hooks/useFileDownloadUrl.js.map +1 -0
- package/lib/module/ui/screens/AccountOverviewScreen.js +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaFAQScreen.js +1 -4
- package/lib/module/ui/screens/karma/KarmaFAQScreen.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 +3 -4
- 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 -9
- 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 -66
- package/lib/typescript/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/ProfileCard.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/index.d.ts +1 -0
- package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useFileDownloadUrl.d.ts +19 -0
- package/lib/typescript/ui/hooks/useFileDownloadUrl.d.ts.map +1 -0
- package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/karma/KarmaFAQScreen.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 +3 -4
- package/src/core/mixins/OxyServices.assets.ts +2 -8
- package/src/core/mixins/OxyServices.user.ts +7 -6
- package/src/core/mixins/OxyServices.utility.ts +1 -0
- package/src/index.ts +1 -0
- package/src/ui/components/ProfileCard.tsx +4 -1
- package/src/ui/hooks/index.ts +2 -1
- package/src/ui/hooks/useFileDownloadUrl.ts +118 -0
- package/src/ui/screens/AccountOverviewScreen.tsx +4 -1
- package/src/ui/screens/karma/KarmaFAQScreen.tsx +1 -5
- 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
package/src/core/HttpClient.ts
DELETED
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP Client Service
|
|
3
|
-
*
|
|
4
|
-
* Handles all HTTP communication with authentication, interceptors, and error handling.
|
|
5
|
-
* This is the single source of truth for making authenticated HTTP requests.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import axios, { type AxiosInstance, type InternalAxiosRequestConfig, type AxiosResponse } from 'axios';
|
|
9
|
-
import { jwtDecode } from 'jwt-decode';
|
|
10
|
-
import type { OxyConfig } from '../models/interfaces';
|
|
11
|
-
import { handleHttpError } from '../utils/errorUtils';
|
|
12
|
-
import { SimpleLogger } from '../utils/requestUtils';
|
|
13
|
-
|
|
14
|
-
interface JwtPayload {
|
|
15
|
-
exp?: number;
|
|
16
|
-
userId?: string;
|
|
17
|
-
id?: string;
|
|
18
|
-
sessionId?: string;
|
|
19
|
-
[key: string]: any;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Token store for authentication
|
|
24
|
-
*/
|
|
25
|
-
class TokenStore {
|
|
26
|
-
private static instance: TokenStore;
|
|
27
|
-
private accessToken: string | null = null;
|
|
28
|
-
private refreshToken: string | null = null;
|
|
29
|
-
|
|
30
|
-
private constructor() {}
|
|
31
|
-
|
|
32
|
-
static getInstance(): TokenStore {
|
|
33
|
-
if (!TokenStore.instance) {
|
|
34
|
-
TokenStore.instance = new TokenStore();
|
|
35
|
-
}
|
|
36
|
-
return TokenStore.instance;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
setTokens(accessToken: string, refreshToken = ''): void {
|
|
40
|
-
this.accessToken = accessToken;
|
|
41
|
-
this.refreshToken = refreshToken;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
getAccessToken(): string | null {
|
|
45
|
-
return this.accessToken;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
getRefreshToken(): string | null {
|
|
49
|
-
return this.refreshToken;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
clearTokens(): void {
|
|
53
|
-
this.accessToken = null;
|
|
54
|
-
this.refreshToken = null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
hasAccessToken(): boolean {
|
|
58
|
-
return !!this.accessToken;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* HTTP Client Service
|
|
64
|
-
*
|
|
65
|
-
* Manages Axios instance with authentication interceptors.
|
|
66
|
-
* All HTTP requests should go through this service to ensure authentication.
|
|
67
|
-
*/
|
|
68
|
-
export class HttpClient {
|
|
69
|
-
private client: AxiosInstance;
|
|
70
|
-
private tokenStore: TokenStore;
|
|
71
|
-
private logger: SimpleLogger;
|
|
72
|
-
private baseURL: string;
|
|
73
|
-
|
|
74
|
-
constructor(config: OxyConfig) {
|
|
75
|
-
this.baseURL = config.baseURL;
|
|
76
|
-
this.tokenStore = TokenStore.getInstance();
|
|
77
|
-
this.logger = new SimpleLogger(
|
|
78
|
-
config.enableLogging || false,
|
|
79
|
-
config.logLevel || 'error',
|
|
80
|
-
'HttpClient'
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const timeout = config.requestTimeout || 5000;
|
|
84
|
-
|
|
85
|
-
// Create Axios instance with optimized configuration
|
|
86
|
-
this.client = axios.create({
|
|
87
|
-
baseURL: config.baseURL,
|
|
88
|
-
timeout,
|
|
89
|
-
headers: {
|
|
90
|
-
'Accept': 'application/json',
|
|
91
|
-
},
|
|
92
|
-
// Enable HTTP keep-alive for connection reuse (Node.js only)
|
|
93
|
-
...(typeof process !== 'undefined' &&
|
|
94
|
-
process.env &&
|
|
95
|
-
typeof window === 'undefined' &&
|
|
96
|
-
typeof require !== 'undefined' ? {
|
|
97
|
-
httpAgent: new (require('http').Agent)({
|
|
98
|
-
keepAlive: true,
|
|
99
|
-
keepAliveMsecs: 1000,
|
|
100
|
-
maxSockets: 50
|
|
101
|
-
}),
|
|
102
|
-
httpsAgent: new (require('https').Agent)({
|
|
103
|
-
keepAlive: true,
|
|
104
|
-
keepAliveMsecs: 1000,
|
|
105
|
-
maxSockets: 50
|
|
106
|
-
}),
|
|
107
|
-
} : {}),
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
this.setupInterceptors();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Setup axios interceptors for authentication and error handling
|
|
115
|
-
*/
|
|
116
|
-
private setupInterceptors(): void {
|
|
117
|
-
// Request interceptor: Add authentication header
|
|
118
|
-
this.client.interceptors.request.use(
|
|
119
|
-
async (req: InternalAxiosRequestConfig) => {
|
|
120
|
-
const accessToken = this.tokenStore.getAccessToken();
|
|
121
|
-
if (!accessToken) {
|
|
122
|
-
return req;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const decoded = jwtDecode<JwtPayload>(accessToken);
|
|
127
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
128
|
-
|
|
129
|
-
// If token expires in less than 60 seconds, refresh it
|
|
130
|
-
if (decoded.exp && decoded.exp - currentTime < 60) {
|
|
131
|
-
if (decoded.sessionId) {
|
|
132
|
-
try {
|
|
133
|
-
// Create a new axios instance to avoid interceptor recursion
|
|
134
|
-
const refreshClient = axios.create({
|
|
135
|
-
baseURL: this.client.defaults.baseURL,
|
|
136
|
-
timeout: this.client.defaults.timeout,
|
|
137
|
-
});
|
|
138
|
-
const res = await refreshClient.get(`/api/session/token/${decoded.sessionId}`);
|
|
139
|
-
this.tokenStore.setTokens(res.data.accessToken);
|
|
140
|
-
req.headers.Authorization = `Bearer ${res.data.accessToken}`;
|
|
141
|
-
this.logger.debug('Token refreshed');
|
|
142
|
-
} catch (refreshError) {
|
|
143
|
-
// If refresh fails, use current token anyway
|
|
144
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
145
|
-
this.logger.warn('Token refresh failed, using current token');
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
152
|
-
}
|
|
153
|
-
} catch (error) {
|
|
154
|
-
this.logger.error('Error processing token:', error);
|
|
155
|
-
// Even if there's an error, still try to use the token
|
|
156
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return req;
|
|
160
|
-
},
|
|
161
|
-
(error) => {
|
|
162
|
-
this.logger.error('Request interceptor error:', error);
|
|
163
|
-
return Promise.reject(error);
|
|
164
|
-
}
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
// Response interceptor: Handle auth errors
|
|
168
|
-
this.client.interceptors.response.use(
|
|
169
|
-
(response) => response,
|
|
170
|
-
(error) => {
|
|
171
|
-
if (error.response?.status === 401) {
|
|
172
|
-
this.logger.warn('401 Unauthorized, clearing tokens');
|
|
173
|
-
this.tokenStore.clearTokens();
|
|
174
|
-
}
|
|
175
|
-
return Promise.reject(error);
|
|
176
|
-
}
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Get the underlying Axios instance
|
|
182
|
-
* Use this only when you need direct access to Axios features
|
|
183
|
-
*/
|
|
184
|
-
getAxiosInstance(): AxiosInstance {
|
|
185
|
-
return this.client;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Make a raw HTTP request (no caching, deduplication, etc.)
|
|
190
|
-
* Use this for requests that need to bypass performance features
|
|
191
|
-
*/
|
|
192
|
-
async request<T = any>(config: {
|
|
193
|
-
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
194
|
-
url: string;
|
|
195
|
-
data?: any;
|
|
196
|
-
params?: any;
|
|
197
|
-
timeout?: number;
|
|
198
|
-
signal?: AbortSignal;
|
|
199
|
-
}): Promise<T> {
|
|
200
|
-
try {
|
|
201
|
-
const response = await this.client.request<T>({
|
|
202
|
-
method: config.method,
|
|
203
|
-
url: config.url,
|
|
204
|
-
data: config.data,
|
|
205
|
-
params: config.params,
|
|
206
|
-
timeout: config.timeout,
|
|
207
|
-
signal: config.signal,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Unwrap standardized API response format: { data: ... }
|
|
211
|
-
// This handles responses from sendSuccess() and sendPaginated() helpers
|
|
212
|
-
const responseData = response.data as any;
|
|
213
|
-
|
|
214
|
-
// Handle paginated responses: { data: [...], pagination: {...} }
|
|
215
|
-
// Return the data array directly - the calling method will wrap it appropriately
|
|
216
|
-
if (responseData && typeof responseData === 'object' && 'data' in responseData && 'pagination' in responseData) {
|
|
217
|
-
// For paginated responses, return the data array directly
|
|
218
|
-
// The calling methods like getUserFollowers/getUserFollowing will handle wrapping
|
|
219
|
-
// We return the whole response so methods can access both data and pagination
|
|
220
|
-
return responseData as T;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Handle regular success responses: { data: ... }
|
|
224
|
-
if (responseData && typeof responseData === 'object' && 'data' in responseData && !Array.isArray(responseData)) {
|
|
225
|
-
return responseData.data as T;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Return as-is for responses that don't use sendSuccess wrapper
|
|
229
|
-
return responseData as T;
|
|
230
|
-
} catch (error) {
|
|
231
|
-
throw handleHttpError(error);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* GET request convenience method
|
|
237
|
-
*/
|
|
238
|
-
async get<T = any>(url: string, config?: { params?: any; timeout?: number; signal?: AbortSignal }): Promise<{ data: T }> {
|
|
239
|
-
const response = await this.request<T>({
|
|
240
|
-
method: 'GET',
|
|
241
|
-
url,
|
|
242
|
-
params: config?.params,
|
|
243
|
-
timeout: config?.timeout,
|
|
244
|
-
signal: config?.signal,
|
|
245
|
-
});
|
|
246
|
-
return { data: response as T };
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* POST request convenience method
|
|
251
|
-
*/
|
|
252
|
-
async post<T = any>(url: string, data?: any, config?: { timeout?: number; signal?: AbortSignal }): Promise<{ data: T }> {
|
|
253
|
-
const response = await this.request<T>({
|
|
254
|
-
method: 'POST',
|
|
255
|
-
url,
|
|
256
|
-
data,
|
|
257
|
-
timeout: config?.timeout,
|
|
258
|
-
signal: config?.signal,
|
|
259
|
-
});
|
|
260
|
-
return { data: response as T };
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* PUT request convenience method
|
|
265
|
-
*/
|
|
266
|
-
async put<T = any>(url: string, data?: any, config?: { timeout?: number; signal?: AbortSignal }): Promise<{ data: T }> {
|
|
267
|
-
const response = await this.request<T>({
|
|
268
|
-
method: 'PUT',
|
|
269
|
-
url,
|
|
270
|
-
data,
|
|
271
|
-
timeout: config?.timeout,
|
|
272
|
-
signal: config?.signal,
|
|
273
|
-
});
|
|
274
|
-
return { data: response as T };
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* PATCH request convenience method
|
|
279
|
-
*/
|
|
280
|
-
async patch<T = any>(url: string, data?: any, config?: { timeout?: number; signal?: AbortSignal }): Promise<{ data: T }> {
|
|
281
|
-
const response = await this.request<T>({
|
|
282
|
-
method: 'PATCH',
|
|
283
|
-
url,
|
|
284
|
-
data,
|
|
285
|
-
timeout: config?.timeout,
|
|
286
|
-
signal: config?.signal,
|
|
287
|
-
});
|
|
288
|
-
return { data: response as T };
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* DELETE request convenience method
|
|
293
|
-
*/
|
|
294
|
-
async delete<T = any>(url: string, config?: { timeout?: number; signal?: AbortSignal }): Promise<{ data: T }> {
|
|
295
|
-
const response = await this.request<T>({
|
|
296
|
-
method: 'DELETE',
|
|
297
|
-
url,
|
|
298
|
-
timeout: config?.timeout,
|
|
299
|
-
signal: config?.signal,
|
|
300
|
-
});
|
|
301
|
-
return { data: response as T };
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Get base URL
|
|
306
|
-
*/
|
|
307
|
-
getBaseURL(): string {
|
|
308
|
-
return this.baseURL;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Set authentication tokens
|
|
313
|
-
*/
|
|
314
|
-
setTokens(accessToken: string, refreshToken = ''): void {
|
|
315
|
-
this.tokenStore.setTokens(accessToken, refreshToken);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Clear authentication tokens
|
|
320
|
-
*/
|
|
321
|
-
clearTokens(): void {
|
|
322
|
-
this.tokenStore.clearTokens();
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Get access token
|
|
327
|
-
*/
|
|
328
|
-
getAccessToken(): string | null {
|
|
329
|
-
return this.tokenStore.getAccessToken();
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Check if has access token
|
|
334
|
-
*/
|
|
335
|
-
hasAccessToken(): boolean {
|
|
336
|
-
return this.tokenStore.hasAccessToken();
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Test-only utility to reset global tokens between jest tests
|
|
340
|
-
static __resetTokensForTests(): void {
|
|
341
|
-
try {
|
|
342
|
-
TokenStore.getInstance().clearTokens();
|
|
343
|
-
} catch {}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Request Manager
|
|
3
|
-
*
|
|
4
|
-
* Handles request-level optimizations: caching, deduplication, queuing, and retry.
|
|
5
|
-
* Works on top of HttpClient to add performance features.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { AxiosInstance } from 'axios';
|
|
9
|
-
import { TTLCache, registerCacheForCleanup } from '../utils/cache';
|
|
10
|
-
import { RequestDeduplicator, RequestQueue, SimpleLogger } from '../utils/requestUtils';
|
|
11
|
-
import { retryAsync } from '../utils/asyncUtils';
|
|
12
|
-
import type { OxyConfig } from '../models/interfaces';
|
|
13
|
-
|
|
14
|
-
export interface RequestOptions {
|
|
15
|
-
cache?: boolean;
|
|
16
|
-
cacheTTL?: number;
|
|
17
|
-
deduplicate?: boolean;
|
|
18
|
-
retry?: boolean;
|
|
19
|
-
maxRetries?: number;
|
|
20
|
-
timeout?: number;
|
|
21
|
-
signal?: AbortSignal;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Request Manager
|
|
26
|
-
*
|
|
27
|
-
* Manages request-level optimizations while delegating actual HTTP calls to HttpClient.
|
|
28
|
-
*/
|
|
29
|
-
export class RequestManager {
|
|
30
|
-
private cache: TTLCache<any>;
|
|
31
|
-
private deduplicator: RequestDeduplicator;
|
|
32
|
-
private requestQueue: RequestQueue;
|
|
33
|
-
private logger: SimpleLogger;
|
|
34
|
-
private config: OxyConfig;
|
|
35
|
-
private httpClient: { request: (config: any) => Promise<any> };
|
|
36
|
-
|
|
37
|
-
// Performance monitoring
|
|
38
|
-
private requestMetrics = {
|
|
39
|
-
totalRequests: 0,
|
|
40
|
-
successfulRequests: 0,
|
|
41
|
-
failedRequests: 0,
|
|
42
|
-
cacheHits: 0,
|
|
43
|
-
cacheMisses: 0,
|
|
44
|
-
averageResponseTime: 0,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
constructor(
|
|
48
|
-
httpClient: { request: (config: any) => Promise<any> },
|
|
49
|
-
config: OxyConfig
|
|
50
|
-
) {
|
|
51
|
-
this.httpClient = httpClient;
|
|
52
|
-
this.config = config;
|
|
53
|
-
|
|
54
|
-
// Initialize performance infrastructure
|
|
55
|
-
this.cache = new TTLCache<any>(config.cacheTTL || 5 * 60 * 1000);
|
|
56
|
-
registerCacheForCleanup(this.cache);
|
|
57
|
-
this.deduplicator = new RequestDeduplicator();
|
|
58
|
-
this.requestQueue = new RequestQueue(
|
|
59
|
-
config.maxConcurrentRequests || 10,
|
|
60
|
-
config.requestQueueSize || 100
|
|
61
|
-
);
|
|
62
|
-
this.logger = new SimpleLogger(
|
|
63
|
-
config.enableLogging || false,
|
|
64
|
-
config.logLevel || 'error',
|
|
65
|
-
'RequestManager'
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Make a request with all performance optimizations
|
|
71
|
-
*/
|
|
72
|
-
async request<T>(
|
|
73
|
-
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
|
|
74
|
-
url: string,
|
|
75
|
-
data?: any,
|
|
76
|
-
options: RequestOptions = {}
|
|
77
|
-
): Promise<T> {
|
|
78
|
-
const {
|
|
79
|
-
cache = method === 'GET', // Cache GET requests by default
|
|
80
|
-
cacheTTL,
|
|
81
|
-
deduplicate = true,
|
|
82
|
-
retry = this.config.enableRetry !== false,
|
|
83
|
-
maxRetries = this.config.maxRetries || 3,
|
|
84
|
-
timeout,
|
|
85
|
-
signal,
|
|
86
|
-
} = options;
|
|
87
|
-
|
|
88
|
-
// Generate cache key
|
|
89
|
-
const cacheKey = cache ? `${method}:${url}:${JSON.stringify(data || {})}` : null;
|
|
90
|
-
|
|
91
|
-
// Check cache first
|
|
92
|
-
if (cache && cacheKey) {
|
|
93
|
-
const cached = this.cache.get(cacheKey) as T | null;
|
|
94
|
-
if (cached !== null) {
|
|
95
|
-
this.requestMetrics.cacheHits++;
|
|
96
|
-
this.logger.debug('Cache hit:', url);
|
|
97
|
-
return cached;
|
|
98
|
-
}
|
|
99
|
-
this.requestMetrics.cacheMisses++;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Request function that uses HttpClient
|
|
103
|
-
const requestFn = async (): Promise<T> => {
|
|
104
|
-
const startTime = Date.now();
|
|
105
|
-
try {
|
|
106
|
-
const result = await this.httpClient.request({
|
|
107
|
-
method,
|
|
108
|
-
url,
|
|
109
|
-
data: method !== 'GET' ? data : undefined,
|
|
110
|
-
params: method === 'GET' ? data : undefined,
|
|
111
|
-
timeout: timeout || this.config.requestTimeout || 5000,
|
|
112
|
-
signal,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
const duration = Date.now() - startTime;
|
|
116
|
-
this.updateMetrics(true, duration);
|
|
117
|
-
this.config.onRequestEnd?.(url, method, duration, true);
|
|
118
|
-
|
|
119
|
-
return result as T;
|
|
120
|
-
} catch (error: any) {
|
|
121
|
-
const duration = Date.now() - startTime;
|
|
122
|
-
this.updateMetrics(false, duration);
|
|
123
|
-
this.config.onRequestEnd?.(url, method, duration, false);
|
|
124
|
-
this.config.onRequestError?.(url, method, error);
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// Wrap with retry if enabled
|
|
130
|
-
const requestWithRetry = retry
|
|
131
|
-
? () => retryAsync(requestFn, maxRetries, this.config.retryDelay || 1000)
|
|
132
|
-
: requestFn;
|
|
133
|
-
|
|
134
|
-
// Wrap with deduplication if enabled
|
|
135
|
-
const dedupeKey = deduplicate ? `${method}:${url}:${JSON.stringify(data || {})}` : null;
|
|
136
|
-
const finalRequest = dedupeKey
|
|
137
|
-
? () => this.deduplicator.deduplicate(dedupeKey, requestWithRetry)
|
|
138
|
-
: requestWithRetry;
|
|
139
|
-
|
|
140
|
-
// Execute request (with queue if needed)
|
|
141
|
-
const result = await this.requestQueue.enqueue(finalRequest);
|
|
142
|
-
|
|
143
|
-
// Cache the result if caching is enabled
|
|
144
|
-
if (cache && cacheKey && result) {
|
|
145
|
-
this.cache.set(cacheKey, result, cacheTTL);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return result;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Update request metrics
|
|
154
|
-
*/
|
|
155
|
-
private updateMetrics(success: boolean, duration: number): void {
|
|
156
|
-
this.requestMetrics.totalRequests++;
|
|
157
|
-
if (success) {
|
|
158
|
-
this.requestMetrics.successfulRequests++;
|
|
159
|
-
} else {
|
|
160
|
-
this.requestMetrics.failedRequests++;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Update average response time (exponential moving average)
|
|
164
|
-
const alpha = 0.1; // Smoothing factor
|
|
165
|
-
this.requestMetrics.averageResponseTime =
|
|
166
|
-
this.requestMetrics.averageResponseTime * (1 - alpha) + duration * alpha;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Get performance metrics
|
|
171
|
-
*/
|
|
172
|
-
getMetrics(): typeof this.requestMetrics {
|
|
173
|
-
return { ...this.requestMetrics };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Clear request cache
|
|
178
|
-
*/
|
|
179
|
-
clearCache(): void {
|
|
180
|
-
this.cache.clear();
|
|
181
|
-
this.logger.debug('Cache cleared');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Clear specific cache entry
|
|
186
|
-
*/
|
|
187
|
-
clearCacheEntry(key: string): void {
|
|
188
|
-
this.cache.delete(key);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Get cache statistics
|
|
193
|
-
*/
|
|
194
|
-
getCacheStats(): { size: number; hits: number; misses: number; hitRate: number } {
|
|
195
|
-
const cacheStats = this.cache.getStats();
|
|
196
|
-
const total = this.requestMetrics.cacheHits + this.requestMetrics.cacheMisses;
|
|
197
|
-
return {
|
|
198
|
-
size: cacheStats.size,
|
|
199
|
-
hits: this.requestMetrics.cacheHits,
|
|
200
|
-
misses: this.requestMetrics.cacheMisses,
|
|
201
|
-
hitRate: total > 0 ? this.requestMetrics.cacheHits / total : 0,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|