@adatechnology/http-client 0.0.1
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/CHANGELOG.md +7 -0
- package/README.md +596 -0
- package/dist/http.interface.js +27 -0
- package/dist/http.module.js +37 -0
- package/dist/http.provider.js +137 -0
- package/dist/http.token.js +6 -0
- package/dist/implementations/axios/axios.http.module.js +41 -0
- package/dist/implementations/axios/axios.http.provider.js +424 -0
- package/dist/implementations/axios/axios.http.token.js +4 -0
- package/dist/implementations/http.implementation.module.js +20 -0
- package/dist/index.js +11 -0
- package/dist/types/http.interface.d.ts +175 -0
- package/dist/types/http.module.d.ts +9 -0
- package/dist/types/http.provider.d.ts +43 -0
- package/dist/types/http.token.d.ts +3 -0
- package/dist/types/implementations/axios/axios.http.module.d.ts +5 -0
- package/dist/types/implementations/axios/axios.http.provider.d.ts +226 -0
- package/dist/types/implementations/axios/axios.http.token.d.ts +1 -0
- package/dist/types/implementations/http.implementation.module.d.ts +2 -0
- package/dist/types/index.d.ts +4 -0
- package/package.json +19 -0
- package/src/http.interface.ts +259 -0
- package/src/http.module.ts +27 -0
- package/src/http.provider.ts +219 -0
- package/src/http.token.ts +3 -0
- package/src/implementations/axios/axios.http.module.ts +33 -0
- package/src/implementations/axios/axios.http.provider.ts +603 -0
- package/src/implementations/axios/axios.http.token.ts +1 -0
- package/src/implementations/http.implementation.module.ts +9 -0
- package/src/index.ts +8 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
import axios, { AxiosInstance, AxiosResponse } from "axios";
|
|
2
|
+
import { from, Observable } from "rxjs";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ErrorInterceptor,
|
|
6
|
+
HttpProviderInterface,
|
|
7
|
+
HttpRequestConfig,
|
|
8
|
+
HttpResponse,
|
|
9
|
+
} from "../../http.interface";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Interface for Axios HTTP Provider
|
|
13
|
+
*/
|
|
14
|
+
export interface AxiosHttpProviderInterface extends HttpProviderInterface {}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Simple cache entry
|
|
18
|
+
*/
|
|
19
|
+
interface CacheEntry<T> {
|
|
20
|
+
data: T;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
ttl: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Axios-based implementation of the HTTP provider interface.
|
|
27
|
+
* Provides HTTP client functionality with both Promise and Observable APIs,
|
|
28
|
+
* plus basic caching capabilities.
|
|
29
|
+
*/
|
|
30
|
+
export class AxiosHttpProvider implements AxiosHttpProviderInterface {
|
|
31
|
+
private axiosInstance: AxiosInstance;
|
|
32
|
+
private errorInterceptors: Map<number, ErrorInterceptor> = new Map();
|
|
33
|
+
private nextErrorInterceptorId = 0;
|
|
34
|
+
private requestInterceptorIds: Set<number> = new Set();
|
|
35
|
+
private responseInterceptorIds: Set<number> = new Set();
|
|
36
|
+
private cache = new Map<string, CacheEntry<any>>();
|
|
37
|
+
private cacheCleanupInterval?: ReturnType<typeof setInterval>;
|
|
38
|
+
|
|
39
|
+
constructor(axiosInstance?: AxiosInstance) {
|
|
40
|
+
this.axiosInstance = axiosInstance || axios.create();
|
|
41
|
+
this.startCacheCleanup();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Starts automatic cache cleanup
|
|
46
|
+
*/
|
|
47
|
+
private startCacheCleanup(): void {
|
|
48
|
+
// Clean expired cache entries every 5 minutes
|
|
49
|
+
this.cacheCleanupInterval = setInterval(() => {
|
|
50
|
+
this.cleanupExpiredCache();
|
|
51
|
+
}, 300000);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Stops automatic cache cleanup
|
|
56
|
+
*/
|
|
57
|
+
private stopCacheCleanup(): void {
|
|
58
|
+
if (this.cacheCleanupInterval) {
|
|
59
|
+
clearInterval(this.cacheCleanupInterval);
|
|
60
|
+
this.cacheCleanupInterval = undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Cleans up expired cache entries
|
|
66
|
+
*/
|
|
67
|
+
private cleanupExpiredCache(): void {
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
const keysToDelete: string[] = [];
|
|
70
|
+
|
|
71
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
72
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
73
|
+
keysToDelete.push(key);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
keysToDelete.forEach((key) => this.cache.delete(key));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generates a cache key from URL and config
|
|
82
|
+
*/
|
|
83
|
+
private generateCacheKey(url: string, config?: HttpRequestConfig): string {
|
|
84
|
+
const params = config?.params ? JSON.stringify(config.params) : "";
|
|
85
|
+
return `${url}${params}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Gets cached data if valid
|
|
90
|
+
*/
|
|
91
|
+
private getCached<T>(key: string): T | null {
|
|
92
|
+
const entry = this.cache.get(key);
|
|
93
|
+
if (!entry) return null;
|
|
94
|
+
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
97
|
+
this.cache.delete(key);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return entry.data;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sets data in cache
|
|
106
|
+
*/
|
|
107
|
+
private setCache<T>(key: string, data: T, ttl: number = 300000): void {
|
|
108
|
+
// 5 minutes default
|
|
109
|
+
this.cache.set(key, {
|
|
110
|
+
data,
|
|
111
|
+
timestamp: Date.now(),
|
|
112
|
+
ttl,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Clears cache for a specific key or all cache
|
|
118
|
+
*/
|
|
119
|
+
clearCache(key?: string): void {
|
|
120
|
+
if (key) {
|
|
121
|
+
this.cache.delete(key);
|
|
122
|
+
} else {
|
|
123
|
+
this.cache.clear();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Gets cache statistics
|
|
129
|
+
*/
|
|
130
|
+
getCacheStats(): { size: number; keys: string[] } {
|
|
131
|
+
return {
|
|
132
|
+
size: this.cache.size,
|
|
133
|
+
keys: Array.from(this.cache.keys()),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Sets a global header that will be included in all requests.
|
|
139
|
+
*/
|
|
140
|
+
setGlobalHeader(key: string, value: string): void {
|
|
141
|
+
this.axiosInstance.defaults.headers.common[key] = value;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Removes a global header.
|
|
146
|
+
*/
|
|
147
|
+
removeGlobalHeader(key: string): void {
|
|
148
|
+
delete this.axiosInstance.defaults.headers.common[key];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Gets all global headers currently set.
|
|
153
|
+
*/
|
|
154
|
+
getGlobalHeaders(): Record<string, string> {
|
|
155
|
+
return { ...this.axiosInstance.defaults.headers.common } as Record<
|
|
156
|
+
string,
|
|
157
|
+
string
|
|
158
|
+
>;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Sets the base URL for all requests.
|
|
163
|
+
*/
|
|
164
|
+
setBaseUrl(baseUrl: string): void {
|
|
165
|
+
this.axiosInstance.defaults.baseURL = baseUrl;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Gets the current base URL.
|
|
170
|
+
*/
|
|
171
|
+
getBaseUrl(): string {
|
|
172
|
+
return this.axiosInstance.defaults.baseURL || "";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Sets the default timeout for all requests.
|
|
177
|
+
*/
|
|
178
|
+
setDefaultTimeout(timeout: number): void {
|
|
179
|
+
this.axiosInstance.defaults.timeout = timeout;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Adds an error interceptor for handling errors globally.
|
|
184
|
+
*/
|
|
185
|
+
addErrorInterceptor(interceptor: ErrorInterceptor): number {
|
|
186
|
+
const id = this.nextErrorInterceptorId++;
|
|
187
|
+
this.errorInterceptors.set(id, interceptor);
|
|
188
|
+
return id;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Adds a request interceptor to the underlying Axios instance.
|
|
193
|
+
*/
|
|
194
|
+
addRequestInterceptor(
|
|
195
|
+
onFulfilled: (config: any) => any,
|
|
196
|
+
onRejected?: (error: any) => any,
|
|
197
|
+
): number {
|
|
198
|
+
const id = this.axiosInstance.interceptors.request.use(
|
|
199
|
+
onFulfilled,
|
|
200
|
+
onRejected,
|
|
201
|
+
);
|
|
202
|
+
this.requestInterceptorIds.add(id);
|
|
203
|
+
return id;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Removes a request interceptor by id.
|
|
208
|
+
*/
|
|
209
|
+
removeRequestInterceptor(id: number): void {
|
|
210
|
+
this.axiosInstance.interceptors.request.eject(id);
|
|
211
|
+
this.requestInterceptorIds.delete(id);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Adds a response interceptor to the underlying Axios instance.
|
|
216
|
+
*/
|
|
217
|
+
addResponseInterceptor(
|
|
218
|
+
onFulfilled: (res: any) => any,
|
|
219
|
+
onRejected?: (error: any) => any,
|
|
220
|
+
): number {
|
|
221
|
+
const id = this.axiosInstance.interceptors.response.use(
|
|
222
|
+
onFulfilled,
|
|
223
|
+
onRejected,
|
|
224
|
+
);
|
|
225
|
+
this.responseInterceptorIds.add(id);
|
|
226
|
+
return id;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Removes a response interceptor by id.
|
|
231
|
+
*/
|
|
232
|
+
removeResponseInterceptor(id: number): void {
|
|
233
|
+
this.axiosInstance.interceptors.response.eject(id);
|
|
234
|
+
this.responseInterceptorIds.delete(id);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Removes an error interceptor by its ID.
|
|
239
|
+
*/
|
|
240
|
+
removeErrorInterceptor(id: number): void {
|
|
241
|
+
this.errorInterceptors.delete(id);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Performs a GET request to the specified URL.
|
|
246
|
+
*/
|
|
247
|
+
async get<T>(
|
|
248
|
+
url: string,
|
|
249
|
+
config?: HttpRequestConfig,
|
|
250
|
+
): Promise<HttpResponse<T>> {
|
|
251
|
+
const cacheKey = this.generateCacheKey(url, config);
|
|
252
|
+
const cached = this.getCached<T>(cacheKey);
|
|
253
|
+
if (cached) {
|
|
254
|
+
return {
|
|
255
|
+
data: cached,
|
|
256
|
+
status: 200,
|
|
257
|
+
statusText: "OK",
|
|
258
|
+
headers: {},
|
|
259
|
+
config: config || { url },
|
|
260
|
+
} as HttpResponse<T>;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return this.wrapWithErrorInterceptors(() =>
|
|
264
|
+
this.performGet<T>(url, config, cacheKey),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Internal method to perform the actual GET request.
|
|
270
|
+
*/
|
|
271
|
+
private async performGet<T>(
|
|
272
|
+
url: string,
|
|
273
|
+
config?: HttpRequestConfig,
|
|
274
|
+
cacheKey?: string,
|
|
275
|
+
): Promise<HttpResponse<T>> {
|
|
276
|
+
const response = await this.axiosInstance.get<T>(url, config);
|
|
277
|
+
const transformed = this.transformResponse<T>(response);
|
|
278
|
+
|
|
279
|
+
if (cacheKey && config?.cache !== false) {
|
|
280
|
+
this.setCache(cacheKey, transformed.data, config?.cacheTtl);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return transformed;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Performs a GET request and returns an Observable.
|
|
288
|
+
*/
|
|
289
|
+
get$<T>(
|
|
290
|
+
url: string,
|
|
291
|
+
config?: HttpRequestConfig,
|
|
292
|
+
): Observable<HttpResponse<T>> {
|
|
293
|
+
return from(this.get<T>(url, config));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Performs a POST request.
|
|
298
|
+
*/
|
|
299
|
+
async post<T>(
|
|
300
|
+
url: string,
|
|
301
|
+
data?: unknown,
|
|
302
|
+
config?: HttpRequestConfig,
|
|
303
|
+
): Promise<HttpResponse<T>> {
|
|
304
|
+
return this.wrapWithErrorInterceptors(() =>
|
|
305
|
+
this.performPost<T>(url, data, config),
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Internal method to perform the actual POST request.
|
|
311
|
+
*/
|
|
312
|
+
private async performPost<T>(
|
|
313
|
+
url: string,
|
|
314
|
+
data?: unknown,
|
|
315
|
+
config?: HttpRequestConfig,
|
|
316
|
+
): Promise<HttpResponse<T>> {
|
|
317
|
+
const response = await this.axiosInstance.post<T>(url, data, config);
|
|
318
|
+
return this.transformResponse<T>(response);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Performs a POST request and returns an Observable.
|
|
323
|
+
*/
|
|
324
|
+
post$<T>(
|
|
325
|
+
url: string,
|
|
326
|
+
data?: unknown,
|
|
327
|
+
config?: HttpRequestConfig,
|
|
328
|
+
): Observable<HttpResponse<T>> {
|
|
329
|
+
return from(this.post<T>(url, data, config));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Performs a PUT request.
|
|
334
|
+
*/
|
|
335
|
+
async put<T>(
|
|
336
|
+
url: string,
|
|
337
|
+
data?: unknown,
|
|
338
|
+
config?: HttpRequestConfig,
|
|
339
|
+
): Promise<HttpResponse<T>> {
|
|
340
|
+
return this.wrapWithErrorInterceptors(() =>
|
|
341
|
+
this.performPut<T>(url, data, config),
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Internal method to perform the actual PUT request.
|
|
347
|
+
*/
|
|
348
|
+
private async performPut<T>(
|
|
349
|
+
url: string,
|
|
350
|
+
data?: unknown,
|
|
351
|
+
config?: HttpRequestConfig,
|
|
352
|
+
): Promise<HttpResponse<T>> {
|
|
353
|
+
const response = await this.axiosInstance.put<T>(url, data, config);
|
|
354
|
+
return this.transformResponse<T>(response);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Performs a PUT request and returns an Observable.
|
|
359
|
+
*/
|
|
360
|
+
put$<T>(
|
|
361
|
+
url: string,
|
|
362
|
+
data?: unknown,
|
|
363
|
+
config?: HttpRequestConfig,
|
|
364
|
+
): Observable<HttpResponse<T>> {
|
|
365
|
+
return from(this.put<T>(url, data, config));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Performs a PATCH request.
|
|
370
|
+
*/
|
|
371
|
+
async patch<T>(
|
|
372
|
+
url: string,
|
|
373
|
+
data?: unknown,
|
|
374
|
+
config?: HttpRequestConfig,
|
|
375
|
+
): Promise<HttpResponse<T>> {
|
|
376
|
+
return this.wrapWithErrorInterceptors(() =>
|
|
377
|
+
this.performPatch<T>(url, data, config),
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Internal method to perform the actual PATCH request.
|
|
383
|
+
*/
|
|
384
|
+
private async performPatch<T>(
|
|
385
|
+
url: string,
|
|
386
|
+
data?: unknown,
|
|
387
|
+
config?: HttpRequestConfig,
|
|
388
|
+
): Promise<HttpResponse<T>> {
|
|
389
|
+
const response = await this.axiosInstance.patch<T>(url, data, config);
|
|
390
|
+
return this.transformResponse<T>(response);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Performs a PATCH request and returns an Observable.
|
|
395
|
+
*/
|
|
396
|
+
patch$<T>(
|
|
397
|
+
url: string,
|
|
398
|
+
data?: unknown,
|
|
399
|
+
config?: HttpRequestConfig,
|
|
400
|
+
): Observable<HttpResponse<T>> {
|
|
401
|
+
return from(this.patch<T>(url, data, config));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Performs a DELETE request.
|
|
406
|
+
*/
|
|
407
|
+
async delete<T>(
|
|
408
|
+
url: string,
|
|
409
|
+
config?: HttpRequestConfig,
|
|
410
|
+
): Promise<HttpResponse<T>> {
|
|
411
|
+
return this.wrapWithErrorInterceptors(() =>
|
|
412
|
+
this.performDelete<T>(url, config),
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Internal method to perform the actual DELETE request.
|
|
418
|
+
*/
|
|
419
|
+
private async performDelete<T>(
|
|
420
|
+
url: string,
|
|
421
|
+
config?: HttpRequestConfig,
|
|
422
|
+
): Promise<HttpResponse<T>> {
|
|
423
|
+
const response = await this.axiosInstance.delete<T>(url, config);
|
|
424
|
+
return this.transformResponse<T>(response);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Performs a DELETE request and returns an Observable.
|
|
429
|
+
*/
|
|
430
|
+
delete$<T>(
|
|
431
|
+
url: string,
|
|
432
|
+
config?: HttpRequestConfig,
|
|
433
|
+
): Observable<HttpResponse<T>> {
|
|
434
|
+
return from(this.delete<T>(url, config));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Performs a HEAD request.
|
|
439
|
+
*/
|
|
440
|
+
async head<T>(
|
|
441
|
+
url: string,
|
|
442
|
+
config?: HttpRequestConfig,
|
|
443
|
+
): Promise<HttpResponse<T>> {
|
|
444
|
+
return this.wrapWithErrorInterceptors(() =>
|
|
445
|
+
this.performHead<T>(url, config),
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Internal method to perform the actual HEAD request.
|
|
451
|
+
*/
|
|
452
|
+
private async performHead<T>(
|
|
453
|
+
url: string,
|
|
454
|
+
config?: HttpRequestConfig,
|
|
455
|
+
): Promise<HttpResponse<T>> {
|
|
456
|
+
const response = await this.axiosInstance.head<T>(url, config);
|
|
457
|
+
return this.transformResponse<T>(response);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Performs a HEAD request and returns an Observable.
|
|
462
|
+
*/
|
|
463
|
+
head$<T>(
|
|
464
|
+
url: string,
|
|
465
|
+
config?: HttpRequestConfig,
|
|
466
|
+
): Observable<HttpResponse<T>> {
|
|
467
|
+
return from(this.head<T>(url, config));
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Performs an OPTIONS request.
|
|
472
|
+
*/
|
|
473
|
+
async options<T>(
|
|
474
|
+
url: string,
|
|
475
|
+
config?: HttpRequestConfig,
|
|
476
|
+
): Promise<HttpResponse<T>> {
|
|
477
|
+
return this.wrapWithErrorInterceptors(() =>
|
|
478
|
+
this.performOptions<T>(url, config),
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Internal method to perform the actual OPTIONS request.
|
|
484
|
+
*/
|
|
485
|
+
private async performOptions<T>(
|
|
486
|
+
url: string,
|
|
487
|
+
config?: HttpRequestConfig,
|
|
488
|
+
): Promise<HttpResponse<T>> {
|
|
489
|
+
const response = await this.axiosInstance.options<T>(url, config);
|
|
490
|
+
return this.transformResponse<T>(response);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Performs an OPTIONS request and returns an Observable.
|
|
495
|
+
*/
|
|
496
|
+
options$<T>(
|
|
497
|
+
url: string,
|
|
498
|
+
config?: HttpRequestConfig,
|
|
499
|
+
): Observable<HttpResponse<T>> {
|
|
500
|
+
return from(this.options<T>(url, config));
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Performs a custom HTTP request.
|
|
505
|
+
*/
|
|
506
|
+
async request<T>(config: HttpRequestConfig): Promise<HttpResponse<T>> {
|
|
507
|
+
return this.wrapWithErrorInterceptors(() => this.performRequest<T>(config));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Internal method to perform the actual custom request.
|
|
512
|
+
*/
|
|
513
|
+
private async performRequest<T>(
|
|
514
|
+
config: HttpRequestConfig,
|
|
515
|
+
): Promise<HttpResponse<T>> {
|
|
516
|
+
const response = await this.axiosInstance.request<T>(config);
|
|
517
|
+
return this.transformResponse<T>(response);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Performs a custom HTTP request and returns an Observable.
|
|
522
|
+
*/
|
|
523
|
+
request$<T>(config: HttpRequestConfig): Observable<HttpResponse<T>> {
|
|
524
|
+
return from(this.request<T>(config));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Sets the authorization token for requests.
|
|
529
|
+
*/
|
|
530
|
+
setAuthToken(token: string, type: string = "Bearer"): void {
|
|
531
|
+
this.axiosInstance.defaults.headers.common["Authorization"] =
|
|
532
|
+
`${type} ${token}`;
|
|
533
|
+
// Log only the initial part of the token to avoid exposing the full secret in logs
|
|
534
|
+
try {
|
|
535
|
+
const masked = AxiosHttpProvider.maskToken(token);
|
|
536
|
+
|
|
537
|
+
console.debug(`[AxiosHttpProvider] setAuthToken ${type} ${masked}`);
|
|
538
|
+
} catch (err) {
|
|
539
|
+
// swallow logging errors
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Clears the authorization token.
|
|
545
|
+
*/
|
|
546
|
+
clearAuthToken(): void {
|
|
547
|
+
delete this.axiosInstance.defaults.headers.common["Authorization"];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Processes an error through all registered error interceptors.
|
|
552
|
+
*/
|
|
553
|
+
private async processErrorInterceptors(error: unknown): Promise<unknown> {
|
|
554
|
+
let processedError = error;
|
|
555
|
+
|
|
556
|
+
for (const interceptor of this.errorInterceptors.values()) {
|
|
557
|
+
try {
|
|
558
|
+
processedError = await interceptor(processedError);
|
|
559
|
+
} catch (interceptorError) {
|
|
560
|
+
console.warn("Error interceptor failed:", interceptorError);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return processedError;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Returns a masked version of the token showing only the initial characters.
|
|
569
|
+
*/
|
|
570
|
+
private static maskToken(token: string, visibleChars = 8): string {
|
|
571
|
+
if (!token || typeof token !== "string") return "";
|
|
572
|
+
return token.length <= visibleChars
|
|
573
|
+
? token
|
|
574
|
+
: `${token.slice(0, visibleChars)}...`;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Wraps a promise-returning HTTP method with error interceptor processing.
|
|
579
|
+
*/
|
|
580
|
+
private async wrapWithErrorInterceptors<T>(
|
|
581
|
+
method: () => Promise<HttpResponse<T>>,
|
|
582
|
+
): Promise<HttpResponse<T>> {
|
|
583
|
+
try {
|
|
584
|
+
return await method();
|
|
585
|
+
} catch (error) {
|
|
586
|
+
const processedError = await this.processErrorInterceptors(error);
|
|
587
|
+
throw processedError;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Transforms an Axios response to the standardized HTTP response format.
|
|
593
|
+
*/
|
|
594
|
+
private transformResponse<T>(response: AxiosResponse<T>): HttpResponse<T> {
|
|
595
|
+
return {
|
|
596
|
+
data: response.data,
|
|
597
|
+
status: response.status,
|
|
598
|
+
statusText: response.statusText,
|
|
599
|
+
headers: response.headers as Record<string, string>,
|
|
600
|
+
config: response.config as HttpRequestConfig,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const AXIOS_HTTP_PROVIDER = 'AXIOS_HTTP_PROVIDER';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common";
|
|
2
|
+
|
|
3
|
+
import { HttpImplementationAxiosModule } from "./axios/axios.http.module";
|
|
4
|
+
|
|
5
|
+
@Module({
|
|
6
|
+
imports: [HttpImplementationAxiosModule],
|
|
7
|
+
exports: [HttpImplementationAxiosModule],
|
|
8
|
+
})
|
|
9
|
+
export class HttpImplementationModule {}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { HttpModule } from "./http.module";
|
|
2
|
+
export { HttpImplementationAxiosModule } from "./implementations/axios/axios.http.module";
|
|
3
|
+
export {
|
|
4
|
+
HTTP_PROVIDER,
|
|
5
|
+
HTTP_AXIOS_PROVIDER,
|
|
6
|
+
HTTP_AXIOS_CONNECTION,
|
|
7
|
+
} from "./http.token";
|
|
8
|
+
export type { HttpProviderInterface } from "./http.interface";
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"declarationDir": "dist/types",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"composite": true
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"./src/**/*.ts"
|
|
14
|
+
],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|