create-request 0.0.1-beta.8 → 1.0.0
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 +538 -331
- package/dist/library/BaseRequest.d.ts +275 -41
- package/dist/library/BodyRequest.d.ts +27 -4
- package/dist/library/RequestError.d.ts +5 -2
- package/dist/library/ResponseWrapper.d.ts +66 -13
- package/dist/library/enums.d.ts +0 -11
- package/dist/library/index.cjs +2109 -0
- package/dist/library/index.cjs.map +1 -0
- package/dist/library/index.d.ts +12 -12
- package/dist/library/index.esm.js +1322 -723
- package/dist/library/index.esm.js.map +1 -0
- package/dist/library/index.esm.min.js +1 -0
- package/dist/library/index.esm.min.js.map +1 -0
- package/dist/library/index.min.cjs +1 -0
- package/dist/library/index.min.cjs.map +1 -0
- package/dist/library/requestFactories.d.ts +70 -9
- package/dist/library/requestMethods.d.ts +70 -3
- package/dist/library/types.d.ts +44 -5
- package/dist/library/utils/Config.d.ts +164 -12
- package/dist/library/utils/CookieUtils.d.ts +1 -1
- package/dist/library/utils/sizeUtils.d.ts +8 -0
- package/package.json +74 -16
- package/dist/library/index.js +0 -1522
- package/dist/library/types/cache.d.ts +0 -62
- package/dist/library/utils/CacheManager.d.ts +0 -35
- package/dist/library/utils/CacheStorage.d.ts +0 -57
- package/dist/library/utils/StorageProvider.d.ts +0 -33
package/dist/library/index.js
DELETED
|
@@ -1,1522 +0,0 @@
|
|
|
1
|
-
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.createRequest = {}));
|
|
5
|
-
})(this, (function (exports) { 'use strict';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Enum for HTTP methods
|
|
9
|
-
*/
|
|
10
|
-
exports.HttpMethod = void 0;
|
|
11
|
-
(function (HttpMethod) {
|
|
12
|
-
HttpMethod["GET"] = "GET";
|
|
13
|
-
HttpMethod["PUT"] = "PUT";
|
|
14
|
-
HttpMethod["POST"] = "POST";
|
|
15
|
-
HttpMethod["HEAD"] = "HEAD";
|
|
16
|
-
HttpMethod["PATCH"] = "PATCH";
|
|
17
|
-
HttpMethod["DELETE"] = "DELETE";
|
|
18
|
-
HttpMethod["OPTIONS"] = "OPTIONS";
|
|
19
|
-
})(exports.HttpMethod || (exports.HttpMethod = {}));
|
|
20
|
-
/**
|
|
21
|
-
* Enum for request priorities
|
|
22
|
-
*/
|
|
23
|
-
exports.RequestPriority = void 0;
|
|
24
|
-
(function (RequestPriority) {
|
|
25
|
-
RequestPriority["LOW"] = "low";
|
|
26
|
-
RequestPriority["HIGH"] = "high";
|
|
27
|
-
RequestPriority["AUTO"] = "auto";
|
|
28
|
-
})(exports.RequestPriority || (exports.RequestPriority = {}));
|
|
29
|
-
/**
|
|
30
|
-
* Enum for credentials policies
|
|
31
|
-
*/
|
|
32
|
-
exports.CredentialsPolicy = void 0;
|
|
33
|
-
(function (CredentialsPolicy) {
|
|
34
|
-
CredentialsPolicy["OMIT"] = "omit";
|
|
35
|
-
CredentialsPolicy["INCLUDE"] = "include";
|
|
36
|
-
CredentialsPolicy["SAME_ORIGIN"] = "same-origin";
|
|
37
|
-
})(exports.CredentialsPolicy || (exports.CredentialsPolicy = {}));
|
|
38
|
-
/**
|
|
39
|
-
* Enum for request modes
|
|
40
|
-
*/
|
|
41
|
-
exports.RequestMode = void 0;
|
|
42
|
-
(function (RequestMode) {
|
|
43
|
-
RequestMode["CORS"] = "cors";
|
|
44
|
-
RequestMode["NO_CORS"] = "no-cors";
|
|
45
|
-
RequestMode["SAME_ORIGIN"] = "same-origin";
|
|
46
|
-
RequestMode["NAVIGATE"] = "navigate";
|
|
47
|
-
})(exports.RequestMode || (exports.RequestMode = {}));
|
|
48
|
-
/**
|
|
49
|
-
* Enum for redirect modes
|
|
50
|
-
*/
|
|
51
|
-
exports.RedirectMode = void 0;
|
|
52
|
-
(function (RedirectMode) {
|
|
53
|
-
RedirectMode["ERROR"] = "error";
|
|
54
|
-
RedirectMode["FOLLOW"] = "follow";
|
|
55
|
-
RedirectMode["MANUAL"] = "manual";
|
|
56
|
-
})(exports.RedirectMode || (exports.RedirectMode = {}));
|
|
57
|
-
/**
|
|
58
|
-
* Enum for cache modes
|
|
59
|
-
*/
|
|
60
|
-
exports.CacheMode = void 0;
|
|
61
|
-
(function (CacheMode) {
|
|
62
|
-
CacheMode["DEFAULT"] = "default";
|
|
63
|
-
CacheMode["RELOAD"] = "reload";
|
|
64
|
-
CacheMode["NO_STORE"] = "no-store";
|
|
65
|
-
CacheMode["NO_CACHE"] = "no-cache";
|
|
66
|
-
CacheMode["FORCE_CACHE"] = "force-cache";
|
|
67
|
-
CacheMode["ONLY_IF_CACHED"] = "only-if-cached";
|
|
68
|
-
})(exports.CacheMode || (exports.CacheMode = {}));
|
|
69
|
-
/**
|
|
70
|
-
* Enum for cookie SameSite policies
|
|
71
|
-
*/
|
|
72
|
-
exports.SameSitePolicy = void 0;
|
|
73
|
-
(function (SameSitePolicy) {
|
|
74
|
-
SameSitePolicy["LAX"] = "Lax";
|
|
75
|
-
SameSitePolicy["NONE"] = "None";
|
|
76
|
-
SameSitePolicy["STRICT"] = "Strict";
|
|
77
|
-
})(exports.SameSitePolicy || (exports.SameSitePolicy = {}));
|
|
78
|
-
/**
|
|
79
|
-
* Enum for body types
|
|
80
|
-
*/
|
|
81
|
-
var BodyType;
|
|
82
|
-
(function (BodyType) {
|
|
83
|
-
BodyType["JSON"] = "json";
|
|
84
|
-
BodyType["STRING"] = "string";
|
|
85
|
-
BodyType["BINARY"] = "binary";
|
|
86
|
-
})(BodyType || (BodyType = {}));
|
|
87
|
-
/**
|
|
88
|
-
* Referrer policies for fetch requests
|
|
89
|
-
*/
|
|
90
|
-
var ReferrerPolicy;
|
|
91
|
-
(function (ReferrerPolicy) {
|
|
92
|
-
ReferrerPolicy["ORIGIN"] = "origin";
|
|
93
|
-
ReferrerPolicy["UNSAFE_URL"] = "unsafe-url";
|
|
94
|
-
ReferrerPolicy["SAME_ORIGIN"] = "same-origin";
|
|
95
|
-
ReferrerPolicy["NO_REFERRER"] = "no-referrer";
|
|
96
|
-
ReferrerPolicy["STRICT_ORIGIN"] = "strict-origin";
|
|
97
|
-
ReferrerPolicy["ORIGIN_WHEN_CROSS_ORIGIN"] = "origin-when-cross-origin";
|
|
98
|
-
ReferrerPolicy["NO_REFERRER_WHEN_DOWNGRADE"] = "no-referrer-when-downgrade";
|
|
99
|
-
ReferrerPolicy["STRICT_ORIGIN_WHEN_CROSS_ORIGIN"] = "strict-origin-when-cross-origin";
|
|
100
|
-
})(ReferrerPolicy || (ReferrerPolicy = {}));
|
|
101
|
-
|
|
102
|
-
class RequestError extends Error {
|
|
103
|
-
constructor(message, url, method, options = {}) {
|
|
104
|
-
super(message);
|
|
105
|
-
this.name = "RequestError";
|
|
106
|
-
this.url = url;
|
|
107
|
-
this.method = method;
|
|
108
|
-
this.status = options.status;
|
|
109
|
-
this.response = options.response;
|
|
110
|
-
this.timeoutError = options.timeoutError;
|
|
111
|
-
// For better stack traces in modern environments
|
|
112
|
-
if (Error.captureStackTrace) {
|
|
113
|
-
Error.captureStackTrace(this, RequestError);
|
|
114
|
-
}
|
|
115
|
-
// Maintains proper prototype chain for instanceof checks
|
|
116
|
-
Object.setPrototypeOf(this, RequestError.prototype);
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Static methods for creating specific types of RequestError
|
|
120
|
-
*/
|
|
121
|
-
static timeout(url, method, timeoutMs) {
|
|
122
|
-
return new RequestError(`Request timed out after ${timeoutMs}ms`, url, method, {
|
|
123
|
-
timeoutError: true,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
static fromResponse(response, url, method) {
|
|
127
|
-
return new RequestError(`Request failed with status ${response.status}`, url, method, {
|
|
128
|
-
status: response.status,
|
|
129
|
-
response,
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
static networkError(url, method, originalError) {
|
|
133
|
-
const error = new RequestError(`Network error: ${originalError.message}`, url, method);
|
|
134
|
-
error.stack = originalError.stack;
|
|
135
|
-
return error;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Wrapper for HTTP responses with methods to transform the response data
|
|
141
|
-
*/
|
|
142
|
-
class ResponseWrapper {
|
|
143
|
-
constructor(response) {
|
|
144
|
-
this.bodyUsed = false;
|
|
145
|
-
this.response = response;
|
|
146
|
-
}
|
|
147
|
-
// Wrapper properties
|
|
148
|
-
get status() {
|
|
149
|
-
return this.response.status;
|
|
150
|
-
}
|
|
151
|
-
get statusText() {
|
|
152
|
-
return this.response.statusText;
|
|
153
|
-
}
|
|
154
|
-
get headers() {
|
|
155
|
-
return this.response.headers;
|
|
156
|
-
}
|
|
157
|
-
get ok() {
|
|
158
|
-
return this.response.ok;
|
|
159
|
-
}
|
|
160
|
-
get raw() {
|
|
161
|
-
return this.response;
|
|
162
|
-
}
|
|
163
|
-
// Response body methods with caching
|
|
164
|
-
async getJson() {
|
|
165
|
-
if (this.jsonCache !== undefined) {
|
|
166
|
-
return this.jsonCache;
|
|
167
|
-
}
|
|
168
|
-
if (this.bodyUsed) {
|
|
169
|
-
// If we already have text, try to parse it as JSON
|
|
170
|
-
if (this.textCache !== undefined) {
|
|
171
|
-
try {
|
|
172
|
-
// Fix unsafe assignment by explicitly casting to unknown first
|
|
173
|
-
this.jsonCache = JSON.parse(this.textCache);
|
|
174
|
-
return this.jsonCache;
|
|
175
|
-
}
|
|
176
|
-
catch (e) {
|
|
177
|
-
throw new Error("Cannot parse cached text as JSON");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
throw new Error("Response body has already been consumed in a different format");
|
|
181
|
-
}
|
|
182
|
-
this.bodyUsed = true;
|
|
183
|
-
this.jsonCache = (await this.response.json());
|
|
184
|
-
return this.jsonCache;
|
|
185
|
-
}
|
|
186
|
-
async getText() {
|
|
187
|
-
if (this.textCache !== undefined) {
|
|
188
|
-
return this.textCache;
|
|
189
|
-
}
|
|
190
|
-
if (this.bodyUsed) {
|
|
191
|
-
// If we already have JSON, convert it to text
|
|
192
|
-
if (this.jsonCache !== undefined) {
|
|
193
|
-
this.textCache = JSON.stringify(this.jsonCache);
|
|
194
|
-
return this.textCache;
|
|
195
|
-
}
|
|
196
|
-
// If we have blob, convert to text
|
|
197
|
-
if (this.blobCache !== undefined) {
|
|
198
|
-
this.textCache = await this.blobCache.text();
|
|
199
|
-
return this.textCache;
|
|
200
|
-
}
|
|
201
|
-
throw new Error("Response body has already been consumed in a format that cannot be converted to text");
|
|
202
|
-
}
|
|
203
|
-
this.bodyUsed = true;
|
|
204
|
-
this.textCache = await this.response.text();
|
|
205
|
-
return this.textCache;
|
|
206
|
-
}
|
|
207
|
-
async getBlob() {
|
|
208
|
-
if (this.blobCache !== undefined) {
|
|
209
|
-
return this.blobCache;
|
|
210
|
-
}
|
|
211
|
-
if (this.bodyUsed) {
|
|
212
|
-
// If we have text, convert to Blob
|
|
213
|
-
if (this.textCache !== undefined) {
|
|
214
|
-
this.blobCache = new Blob([this.textCache], { type: "text/plain" });
|
|
215
|
-
return this.blobCache;
|
|
216
|
-
}
|
|
217
|
-
throw new Error("Response body has already been consumed in a format that cannot be converted to Blob");
|
|
218
|
-
}
|
|
219
|
-
this.bodyUsed = true;
|
|
220
|
-
this.blobCache = await this.response.blob();
|
|
221
|
-
return this.blobCache;
|
|
222
|
-
}
|
|
223
|
-
async getArrayBuffer() {
|
|
224
|
-
if (this.arrayBufferCache !== undefined) {
|
|
225
|
-
return this.arrayBufferCache;
|
|
226
|
-
}
|
|
227
|
-
if (this.bodyUsed) {
|
|
228
|
-
// If we have text, convert to ArrayBuffer
|
|
229
|
-
if (this.textCache !== undefined) {
|
|
230
|
-
const encoder = new TextEncoder();
|
|
231
|
-
// Fix ArrayBufferLike by explicitly casting to ArrayBuffer
|
|
232
|
-
const uint8Array = encoder.encode(this.textCache);
|
|
233
|
-
this.arrayBufferCache = uint8Array.buffer;
|
|
234
|
-
return this.arrayBufferCache;
|
|
235
|
-
}
|
|
236
|
-
// If we have blob, convert to ArrayBuffer
|
|
237
|
-
if (this.blobCache !== undefined) {
|
|
238
|
-
this.arrayBufferCache = await this.blobCache.arrayBuffer();
|
|
239
|
-
return this.arrayBufferCache;
|
|
240
|
-
}
|
|
241
|
-
throw new Error("Response body has already been consumed in a format that cannot be converted to ArrayBuffer");
|
|
242
|
-
}
|
|
243
|
-
this.bodyUsed = true;
|
|
244
|
-
this.arrayBufferCache = await this.response.arrayBuffer();
|
|
245
|
-
return this.arrayBufferCache;
|
|
246
|
-
}
|
|
247
|
-
getBody() {
|
|
248
|
-
if (this.bodyUsed) {
|
|
249
|
-
throw new Error("Response body has already been consumed");
|
|
250
|
-
}
|
|
251
|
-
this.bodyUsed = true;
|
|
252
|
-
return this.response.body;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Parse size strings like "10MB" into bytes
|
|
258
|
-
* @param size Size as number or string with units
|
|
259
|
-
*/
|
|
260
|
-
function parseSize(size) {
|
|
261
|
-
var _a;
|
|
262
|
-
if (typeof size === "number")
|
|
263
|
-
return size;
|
|
264
|
-
const match = size.match(/^(\d+(?:\.\d+)?)\s*(KB|MB|GB)?$/i);
|
|
265
|
-
if (!match)
|
|
266
|
-
return 0;
|
|
267
|
-
const num = parseFloat(match[1]);
|
|
268
|
-
const unit = (_a = match[2]) === null || _a === void 0 ? void 0 : _a.toUpperCase();
|
|
269
|
-
switch (unit) {
|
|
270
|
-
case "KB":
|
|
271
|
-
return num * 1024;
|
|
272
|
-
case "MB":
|
|
273
|
-
return num * 1024 * 1024;
|
|
274
|
-
case "GB":
|
|
275
|
-
return num * 1024 * 1024 * 1024;
|
|
276
|
-
default:
|
|
277
|
-
return num;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Create a memory storage provider using Map
|
|
282
|
-
*/
|
|
283
|
-
function createMemoryStorage() {
|
|
284
|
-
const store = new Map();
|
|
285
|
-
return {
|
|
286
|
-
get(key) {
|
|
287
|
-
return store.get(key) || null;
|
|
288
|
-
},
|
|
289
|
-
set(key, value) {
|
|
290
|
-
store.set(key, value);
|
|
291
|
-
},
|
|
292
|
-
has(key) {
|
|
293
|
-
return store.has(key);
|
|
294
|
-
},
|
|
295
|
-
delete(key) {
|
|
296
|
-
store.delete(key);
|
|
297
|
-
},
|
|
298
|
-
clear() {
|
|
299
|
-
store.clear();
|
|
300
|
-
},
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Create a localStorage-based storage provider
|
|
305
|
-
*/
|
|
306
|
-
function createLocalStorageStorage() {
|
|
307
|
-
return {
|
|
308
|
-
get(key) {
|
|
309
|
-
if (typeof localStorage === "undefined")
|
|
310
|
-
return null;
|
|
311
|
-
return localStorage.getItem(key);
|
|
312
|
-
},
|
|
313
|
-
set(key, value) {
|
|
314
|
-
if (typeof localStorage === "undefined")
|
|
315
|
-
return;
|
|
316
|
-
try {
|
|
317
|
-
localStorage.setItem(key, value);
|
|
318
|
-
}
|
|
319
|
-
catch (e) {
|
|
320
|
-
// Handle quota exceeded errors
|
|
321
|
-
console.warn("Cache storage quota exceeded, clearing cache");
|
|
322
|
-
void this.clear();
|
|
323
|
-
try {
|
|
324
|
-
localStorage.setItem(key, value);
|
|
325
|
-
}
|
|
326
|
-
catch (e) {
|
|
327
|
-
console.error("Failed to set cache item even after clearing cache");
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
has(key) {
|
|
332
|
-
if (typeof localStorage === "undefined")
|
|
333
|
-
return false;
|
|
334
|
-
return localStorage.getItem(key) !== null;
|
|
335
|
-
},
|
|
336
|
-
delete(key) {
|
|
337
|
-
if (typeof localStorage === "undefined")
|
|
338
|
-
return;
|
|
339
|
-
localStorage.removeItem(key);
|
|
340
|
-
},
|
|
341
|
-
clear() {
|
|
342
|
-
if (typeof localStorage === "undefined")
|
|
343
|
-
return;
|
|
344
|
-
localStorage.clear();
|
|
345
|
-
},
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Create a sessionStorage-based storage provider
|
|
350
|
-
*/
|
|
351
|
-
function createSessionStorageStorage() {
|
|
352
|
-
return {
|
|
353
|
-
get(key) {
|
|
354
|
-
if (typeof sessionStorage === "undefined")
|
|
355
|
-
return null;
|
|
356
|
-
return sessionStorage.getItem(key);
|
|
357
|
-
},
|
|
358
|
-
set(key, value) {
|
|
359
|
-
if (typeof sessionStorage === "undefined")
|
|
360
|
-
return;
|
|
361
|
-
try {
|
|
362
|
-
sessionStorage.setItem(key, value);
|
|
363
|
-
}
|
|
364
|
-
catch (e) {
|
|
365
|
-
// Handle quota exceeded errors
|
|
366
|
-
console.warn("Cache storage quota exceeded, clearing cache");
|
|
367
|
-
void this.clear();
|
|
368
|
-
try {
|
|
369
|
-
sessionStorage.setItem(key, value);
|
|
370
|
-
}
|
|
371
|
-
catch (e) {
|
|
372
|
-
console.error("Failed to set cache item even after clearing cache");
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
},
|
|
376
|
-
has(key) {
|
|
377
|
-
if (typeof sessionStorage === "undefined")
|
|
378
|
-
return false;
|
|
379
|
-
return sessionStorage.getItem(key) !== null;
|
|
380
|
-
},
|
|
381
|
-
delete(key) {
|
|
382
|
-
if (typeof sessionStorage === "undefined")
|
|
383
|
-
return;
|
|
384
|
-
sessionStorage.removeItem(key);
|
|
385
|
-
},
|
|
386
|
-
clear() {
|
|
387
|
-
if (typeof sessionStorage === "undefined")
|
|
388
|
-
return;
|
|
389
|
-
sessionStorage.clear();
|
|
390
|
-
},
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Create a storage provider from a cache options object
|
|
395
|
-
*/
|
|
396
|
-
function createStorageProvider(options) {
|
|
397
|
-
const { storage } = options || {};
|
|
398
|
-
if (!storage) {
|
|
399
|
-
// Default to memory storage
|
|
400
|
-
return createMemoryStorage();
|
|
401
|
-
}
|
|
402
|
-
// Check if storage is already a StorageProvider
|
|
403
|
-
if (typeof storage.get === "function" && typeof storage.set === "function") {
|
|
404
|
-
return storage;
|
|
405
|
-
}
|
|
406
|
-
// Check if it's localStorage or sessionStorage
|
|
407
|
-
if (storage === localStorage) {
|
|
408
|
-
return createLocalStorageStorage();
|
|
409
|
-
}
|
|
410
|
-
if (storage === sessionStorage) {
|
|
411
|
-
return createSessionStorageStorage();
|
|
412
|
-
}
|
|
413
|
-
// Check if it's a Map
|
|
414
|
-
if (storage instanceof Map) {
|
|
415
|
-
return {
|
|
416
|
-
get(key) {
|
|
417
|
-
return storage.get(key) || null;
|
|
418
|
-
},
|
|
419
|
-
set(key, value, _ttl) {
|
|
420
|
-
storage.set(key, value);
|
|
421
|
-
},
|
|
422
|
-
has(key) {
|
|
423
|
-
return storage.has(key);
|
|
424
|
-
},
|
|
425
|
-
delete(key) {
|
|
426
|
-
storage.delete(key);
|
|
427
|
-
},
|
|
428
|
-
clear() {
|
|
429
|
-
storage.clear();
|
|
430
|
-
},
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
// Default to memory storage
|
|
434
|
-
console.warn("Unsupported storage type, falling back to memory storage");
|
|
435
|
-
return createMemoryStorage();
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Cache manager that handles storage operations
|
|
440
|
-
*/
|
|
441
|
-
class CacheManager {
|
|
442
|
-
constructor(options = {}) {
|
|
443
|
-
this.entryCount = 0;
|
|
444
|
-
this.totalSize = 0;
|
|
445
|
-
this.options = options;
|
|
446
|
-
this.storage = createStorageProvider(options);
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Generate a cache key based on request details
|
|
450
|
-
*/
|
|
451
|
-
generateKey(url, method, headers, body) {
|
|
452
|
-
// Use custom key generator if provided
|
|
453
|
-
if (this.options.keyGenerator) {
|
|
454
|
-
const customKey = this.options.keyGenerator(url, method, headers, body);
|
|
455
|
-
return this.options.keyPrefix ? `${this.options.keyPrefix}:${customKey}` : customKey;
|
|
456
|
-
}
|
|
457
|
-
// Default key generation
|
|
458
|
-
let key = `${method}:${url}`;
|
|
459
|
-
// Add vary headers to key if specified
|
|
460
|
-
if (this.options.varyByHeaders && headers) {
|
|
461
|
-
const varyHeaders = {};
|
|
462
|
-
this.options.varyByHeaders.forEach(header => {
|
|
463
|
-
const normalizedHeader = header.toLowerCase();
|
|
464
|
-
// Find the header in case-insensitive way
|
|
465
|
-
Object.keys(headers).forEach(h => {
|
|
466
|
-
if (h.toLowerCase() === normalizedHeader) {
|
|
467
|
-
varyHeaders[normalizedHeader] = headers[h];
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
});
|
|
471
|
-
if (Object.keys(varyHeaders).length > 0) {
|
|
472
|
-
key += `:${JSON.stringify(varyHeaders)}`;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
// Include body in cache key for non-GET requests if it's a simple value
|
|
476
|
-
if (method !== "GET" && body !== undefined && body !== null) {
|
|
477
|
-
try {
|
|
478
|
-
if (typeof body === "string" || typeof body === "number" || typeof body === "boolean") {
|
|
479
|
-
key += `:${String(body)}`;
|
|
480
|
-
}
|
|
481
|
-
else if (typeof body === "object") {
|
|
482
|
-
key += `:${JSON.stringify(body)}`;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
catch (e) {
|
|
486
|
-
console.warn("Failed to stringify request body for cache key");
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
// Add prefix if specified
|
|
490
|
-
return this.options.keyPrefix ? `${this.options.keyPrefix}:${key}` : key;
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Get a value from cache
|
|
494
|
-
*/
|
|
495
|
-
async get(key) {
|
|
496
|
-
const value = await Promise.resolve(this.storage.get(key));
|
|
497
|
-
if (!value)
|
|
498
|
-
return null;
|
|
499
|
-
try {
|
|
500
|
-
const entry = JSON.parse(value);
|
|
501
|
-
// Check if the entry has expired
|
|
502
|
-
if (entry.expiry && entry.expiry < Date.now()) {
|
|
503
|
-
await Promise.resolve(this.storage.delete(key));
|
|
504
|
-
return null;
|
|
505
|
-
}
|
|
506
|
-
return entry;
|
|
507
|
-
}
|
|
508
|
-
catch (e) {
|
|
509
|
-
// If we can't parse the cached value, remove it
|
|
510
|
-
await Promise.resolve(this.storage.delete(key));
|
|
511
|
-
return null;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* Set a value in cache
|
|
516
|
-
*/
|
|
517
|
-
async set(key, value, headers) {
|
|
518
|
-
const now = Date.now();
|
|
519
|
-
const ttl = this.options.ttl;
|
|
520
|
-
const entry = {
|
|
521
|
-
value,
|
|
522
|
-
timestamp: now,
|
|
523
|
-
expiry: ttl ? now + ttl : null,
|
|
524
|
-
headers,
|
|
525
|
-
};
|
|
526
|
-
const serialized = JSON.stringify(entry);
|
|
527
|
-
// Check size constraints if applicable
|
|
528
|
-
if (this.options.maxSize) {
|
|
529
|
-
const maxBytes = parseSize(this.options.maxSize);
|
|
530
|
-
const entrySize = new TextEncoder().encode(serialized).length;
|
|
531
|
-
// If a single entry exceeds the max size, reject it without clearing the cache
|
|
532
|
-
if (entrySize > maxBytes) {
|
|
533
|
-
console.warn(`Cache entry size (${entrySize} bytes) exceeds maximum cache size (${maxBytes} bytes)`);
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
// Only clear if adding this entry would exceed the total limit
|
|
537
|
-
if (this.totalSize + entrySize > maxBytes) {
|
|
538
|
-
// Simple strategy: clear everything
|
|
539
|
-
// A more sophisticated strategy would remove oldest entries first
|
|
540
|
-
await Promise.resolve(this.storage.clear());
|
|
541
|
-
this.totalSize = 0;
|
|
542
|
-
this.entryCount = 0;
|
|
543
|
-
}
|
|
544
|
-
this.totalSize += entrySize;
|
|
545
|
-
}
|
|
546
|
-
// Check max entries constraint
|
|
547
|
-
if (this.options.maxEntries) {
|
|
548
|
-
this.entryCount++;
|
|
549
|
-
if (this.entryCount > this.options.maxEntries) {
|
|
550
|
-
// Simple strategy: clear everything
|
|
551
|
-
// A more sophisticated strategy would use LRU cache
|
|
552
|
-
await Promise.resolve(this.storage.clear());
|
|
553
|
-
this.entryCount = 1;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
// Pass ttl to storage provider if available
|
|
557
|
-
await Promise.resolve(this.storage.set(key, serialized, ttl));
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Check if a key exists in the cache
|
|
561
|
-
*/
|
|
562
|
-
async has(key) {
|
|
563
|
-
return Promise.resolve(this.storage.has(key));
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* Delete a key from the cache
|
|
567
|
-
*/
|
|
568
|
-
async delete(key) {
|
|
569
|
-
return Promise.resolve(this.storage.delete(key));
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Clear the entire cache
|
|
573
|
-
*/
|
|
574
|
-
async clear() {
|
|
575
|
-
this.entryCount = 0;
|
|
576
|
-
this.totalSize = 0;
|
|
577
|
-
return Promise.resolve(this.storage.clear());
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Global configuration for create-request
|
|
583
|
-
*/
|
|
584
|
-
class Config {
|
|
585
|
-
constructor() {
|
|
586
|
-
// CSRF configuration
|
|
587
|
-
this.csrfHeaderName = "X-CSRF-Token";
|
|
588
|
-
this.xsrfCookieName = "XSRF-TOKEN";
|
|
589
|
-
this.xsrfHeaderName = "X-XSRF-TOKEN";
|
|
590
|
-
this.csrfToken = null;
|
|
591
|
-
this.enableAutoXsrf = true;
|
|
592
|
-
this.enableAntiCsrf = true; // X-Requested-With header
|
|
593
|
-
}
|
|
594
|
-
/**
|
|
595
|
-
* Get the singleton instance
|
|
596
|
-
*/
|
|
597
|
-
static getInstance() {
|
|
598
|
-
if (!Config.instance) {
|
|
599
|
-
Config.instance = new Config();
|
|
600
|
-
}
|
|
601
|
-
return Config.instance;
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* Set a global CSRF token to be used for all requests
|
|
605
|
-
*/
|
|
606
|
-
setCsrfToken(token) {
|
|
607
|
-
this.csrfToken = token;
|
|
608
|
-
return this;
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* Get the global CSRF token
|
|
612
|
-
*/
|
|
613
|
-
getCsrfToken() {
|
|
614
|
-
return this.csrfToken;
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Set the CSRF header name (default is X-CSRF-Token)
|
|
618
|
-
*/
|
|
619
|
-
setCsrfHeaderName(name) {
|
|
620
|
-
this.csrfHeaderName = name;
|
|
621
|
-
return this;
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Get the CSRF header name
|
|
625
|
-
*/
|
|
626
|
-
getCsrfHeaderName() {
|
|
627
|
-
return this.csrfHeaderName;
|
|
628
|
-
}
|
|
629
|
-
/**
|
|
630
|
-
* Set the XSRF cookie name to look for (default is XSRF-TOKEN)
|
|
631
|
-
*/
|
|
632
|
-
setXsrfCookieName(name) {
|
|
633
|
-
this.xsrfCookieName = name;
|
|
634
|
-
return this;
|
|
635
|
-
}
|
|
636
|
-
/**
|
|
637
|
-
* Get the XSRF cookie name
|
|
638
|
-
*/
|
|
639
|
-
getXsrfCookieName() {
|
|
640
|
-
return this.xsrfCookieName;
|
|
641
|
-
}
|
|
642
|
-
/**
|
|
643
|
-
* Set the XSRF header name to send the token in (default is X-XSRF-TOKEN)
|
|
644
|
-
*/
|
|
645
|
-
setXsrfHeaderName(name) {
|
|
646
|
-
this.xsrfHeaderName = name;
|
|
647
|
-
return this;
|
|
648
|
-
}
|
|
649
|
-
/**
|
|
650
|
-
* Get the XSRF header name
|
|
651
|
-
*/
|
|
652
|
-
getXsrfHeaderName() {
|
|
653
|
-
return this.xsrfHeaderName;
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Enable or disable automatic XSRF token extraction from cookies
|
|
657
|
-
*/
|
|
658
|
-
setEnableAutoXsrf(enable) {
|
|
659
|
-
this.enableAutoXsrf = enable;
|
|
660
|
-
return this;
|
|
661
|
-
}
|
|
662
|
-
/**
|
|
663
|
-
* Check if automatic XSRF is enabled
|
|
664
|
-
*/
|
|
665
|
-
isAutoXsrfEnabled() {
|
|
666
|
-
return this.enableAutoXsrf;
|
|
667
|
-
}
|
|
668
|
-
/**
|
|
669
|
-
* Enable or disable automatic addition of anti-CSRF headers
|
|
670
|
-
*/
|
|
671
|
-
setEnableAntiCsrf(enable) {
|
|
672
|
-
this.enableAntiCsrf = enable;
|
|
673
|
-
return this;
|
|
674
|
-
}
|
|
675
|
-
/**
|
|
676
|
-
* Check if anti-CSRF is enabled
|
|
677
|
-
*/
|
|
678
|
-
isAntiCsrfEnabled() {
|
|
679
|
-
return this.enableAntiCsrf;
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Reset all configuration options to their defaults
|
|
683
|
-
*/
|
|
684
|
-
reset() {
|
|
685
|
-
this.csrfToken = null;
|
|
686
|
-
this.csrfHeaderName = "X-CSRF-Token";
|
|
687
|
-
this.enableAntiCsrf = true;
|
|
688
|
-
this.xsrfCookieName = "XSRF-TOKEN";
|
|
689
|
-
this.xsrfHeaderName = "X-XSRF-TOKEN";
|
|
690
|
-
this.enableAutoXsrf = true;
|
|
691
|
-
return this;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
class CookieUtils {
|
|
696
|
-
/**
|
|
697
|
-
* Formats cookies for a request
|
|
698
|
-
* @param cookies Object containing cookie name-value pairs or cookie options
|
|
699
|
-
* @returns Formatted cookie string for the Cookie header
|
|
700
|
-
*/
|
|
701
|
-
static formatRequestCookies(cookies) {
|
|
702
|
-
const cookiePairs = [];
|
|
703
|
-
Object.entries(cookies).forEach(([name, valueOrOptions]) => {
|
|
704
|
-
let value;
|
|
705
|
-
if (typeof valueOrOptions === "string") {
|
|
706
|
-
value = valueOrOptions;
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
// Extract value from options object without validation
|
|
710
|
-
value = valueOrOptions.value;
|
|
711
|
-
}
|
|
712
|
-
// Add the cookie to the request
|
|
713
|
-
cookiePairs.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);
|
|
714
|
-
});
|
|
715
|
-
return cookiePairs.join("; ");
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/**
|
|
720
|
-
* Utility class for CSRF token management
|
|
721
|
-
*/
|
|
722
|
-
class CsrfUtils {
|
|
723
|
-
/**
|
|
724
|
-
* Extracts CSRF token from a meta tag in the document head
|
|
725
|
-
* @param metaName The name attribute of the meta tag (default: "csrf-token")
|
|
726
|
-
* @returns The CSRF token or null if not found
|
|
727
|
-
*/
|
|
728
|
-
static getTokenFromMeta(metaName = "csrf-token") {
|
|
729
|
-
if (typeof document === "undefined") {
|
|
730
|
-
return null;
|
|
731
|
-
}
|
|
732
|
-
const meta = document.querySelector(`meta[name="${metaName}"]`);
|
|
733
|
-
return (meta === null || meta === void 0 ? void 0 : meta.getAttribute("content")) || null;
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Extracts CSRF token from a cookie
|
|
737
|
-
* @param cookieName The name of the cookie containing the CSRF token
|
|
738
|
-
* @returns The CSRF token or null if not found
|
|
739
|
-
*/
|
|
740
|
-
static getTokenFromCookie(cookieName = "csrf-token") {
|
|
741
|
-
if (typeof document === "undefined") {
|
|
742
|
-
return null;
|
|
743
|
-
}
|
|
744
|
-
const cookies = document.cookie.split(";");
|
|
745
|
-
for (const cookie of cookies) {
|
|
746
|
-
const [name, value] = cookie.trim().split("=");
|
|
747
|
-
if (name === cookieName) {
|
|
748
|
-
return decodeURIComponent(value);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
return null;
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* Validates if the provided string is a potential CSRF token
|
|
755
|
-
* Checks if the token meets security requirements
|
|
756
|
-
* @param token The token to validate
|
|
757
|
-
* @returns Whether the token is valid
|
|
758
|
-
*/
|
|
759
|
-
static isValidToken(token) {
|
|
760
|
-
// Basic checks for null/undefined and type
|
|
761
|
-
if (typeof token !== "string") {
|
|
762
|
-
return false;
|
|
763
|
-
}
|
|
764
|
-
if (token.length < 8)
|
|
765
|
-
return false;
|
|
766
|
-
// If token is longer than 10 chars, perform additional security checks
|
|
767
|
-
if (token.length > 10) {
|
|
768
|
-
// Check for valid character set (alphanumeric & common token symbols)
|
|
769
|
-
const validTokenRegex = /^[A-Za-z0-9\-_=+/.]+$/;
|
|
770
|
-
if (!validTokenRegex.test(token)) {
|
|
771
|
-
return false;
|
|
772
|
-
}
|
|
773
|
-
// For longer tokens, check for sufficient entropy (at least 2 character types)
|
|
774
|
-
const hasUpperCase = /[A-Z]/.test(token);
|
|
775
|
-
const hasLowerCase = /[a-z]/.test(token);
|
|
776
|
-
const hasNumbers = /[0-9]/.test(token);
|
|
777
|
-
const hasSpecials = /[-_=+/.]/.test(token);
|
|
778
|
-
const characterTypesCount = [hasUpperCase, hasLowerCase, hasNumbers, hasSpecials].filter(Boolean).length;
|
|
779
|
-
return characterTypesCount >= 2;
|
|
780
|
-
}
|
|
781
|
-
// For shorter tokens (8-10 chars), just return true if we reached here
|
|
782
|
-
return true;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// Base class with common functionality for all request types
|
|
787
|
-
class BaseRequest {
|
|
788
|
-
constructor() {
|
|
789
|
-
this.requestOptions = {
|
|
790
|
-
headers: {},
|
|
791
|
-
};
|
|
792
|
-
this.queryParams = new URLSearchParams();
|
|
793
|
-
this.autoApplyCsrfProtection = true;
|
|
794
|
-
this.cacheEnabled = false;
|
|
795
|
-
}
|
|
796
|
-
withHeaders(headers) {
|
|
797
|
-
this.requestOptions.headers = {
|
|
798
|
-
...this.requestOptions.headers,
|
|
799
|
-
...headers,
|
|
800
|
-
};
|
|
801
|
-
return this;
|
|
802
|
-
}
|
|
803
|
-
withTimeout(timeout) {
|
|
804
|
-
if (!Number.isFinite(timeout) || timeout <= 0)
|
|
805
|
-
throw new Error("Timeout must be a positive number");
|
|
806
|
-
this.requestOptions.timeout = timeout;
|
|
807
|
-
return this;
|
|
808
|
-
}
|
|
809
|
-
withRetries(retries) {
|
|
810
|
-
if (!Number.isInteger(retries) || retries < 0)
|
|
811
|
-
throw new Error("Retry count must be a non-negative integer");
|
|
812
|
-
this.requestOptions.retries = retries;
|
|
813
|
-
return this;
|
|
814
|
-
}
|
|
815
|
-
onRetry(callback) {
|
|
816
|
-
this.requestOptions.onRetry = callback;
|
|
817
|
-
return this;
|
|
818
|
-
}
|
|
819
|
-
/**
|
|
820
|
-
* Sets the credentials policy for the request
|
|
821
|
-
* @param credentialsPolicy - Can be 'omit', 'same-origin', or 'include'
|
|
822
|
-
* @returns The instance for chaining
|
|
823
|
-
*/
|
|
824
|
-
withCredentials(credentialsPolicy = exports.CredentialsPolicy.SAME_ORIGIN) {
|
|
825
|
-
this.requestOptions.credentials = credentialsPolicy;
|
|
826
|
-
return this;
|
|
827
|
-
}
|
|
828
|
-
/**
|
|
829
|
-
* Allows providing an external AbortController to cancel the request
|
|
830
|
-
* @param controller - The AbortController to use for this request
|
|
831
|
-
* @returns The instance for chaining
|
|
832
|
-
*/
|
|
833
|
-
withAbortController(controller) {
|
|
834
|
-
this.abortController = controller;
|
|
835
|
-
return this;
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Sets the referrer for the request
|
|
839
|
-
* @param referrer - The referrer URL or policy
|
|
840
|
-
* @returns The instance for chaining
|
|
841
|
-
*/
|
|
842
|
-
withReferrer(referrer) {
|
|
843
|
-
this.requestOptions.referrer = referrer;
|
|
844
|
-
return this;
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
|
-
* Sets the referrer policy for the request
|
|
848
|
-
* @param policy - The referrer policy to use
|
|
849
|
-
* @returns The instance for chaining
|
|
850
|
-
*/
|
|
851
|
-
withReferrerPolicy(policy) {
|
|
852
|
-
this.requestOptions.referrerPolicy = policy;
|
|
853
|
-
return this;
|
|
854
|
-
}
|
|
855
|
-
/**
|
|
856
|
-
* Sets the redirect policy for the request
|
|
857
|
-
* @param redirect - Can be 'follow', 'error', or 'manual'
|
|
858
|
-
* @returns The instance for chaining
|
|
859
|
-
*/
|
|
860
|
-
withRedirect(redirect) {
|
|
861
|
-
this.requestOptions.redirect = redirect;
|
|
862
|
-
return this;
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Sets the keepalive flag for the request
|
|
866
|
-
* @param keepalive - Whether to allow the request to outlive the page
|
|
867
|
-
* @returns The instance for chaining
|
|
868
|
-
*/
|
|
869
|
-
withKeepAlive(keepalive) {
|
|
870
|
-
this.requestOptions.keepalive = keepalive;
|
|
871
|
-
return this;
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Sets the priority of the request
|
|
875
|
-
* @param priority - Can be 'high', 'low', or 'auto'
|
|
876
|
-
* @returns The instance for chaining
|
|
877
|
-
*/
|
|
878
|
-
withPriority(priority) {
|
|
879
|
-
this.requestOptions.priority = priority;
|
|
880
|
-
return this;
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Adds query parameters to the request URL
|
|
884
|
-
* @param params - An object containing the query parameters
|
|
885
|
-
* @returns The instance for chaining
|
|
886
|
-
*/
|
|
887
|
-
withQueryParams(params) {
|
|
888
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
889
|
-
if (value === null || value === undefined) {
|
|
890
|
-
return;
|
|
891
|
-
}
|
|
892
|
-
if (Array.isArray(value)) {
|
|
893
|
-
// Handle array values - add multiple entries with the same key
|
|
894
|
-
value.forEach(v => this.queryParams.append(key, String(v)));
|
|
895
|
-
}
|
|
896
|
-
else {
|
|
897
|
-
this.queryParams.append(key, String(value));
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
return this;
|
|
901
|
-
}
|
|
902
|
-
/**
|
|
903
|
-
* Adds a single query parameter
|
|
904
|
-
* @param key - The parameter name
|
|
905
|
-
* @param value - The parameter value, can be a single value or array of values
|
|
906
|
-
* @returns The instance for chaining
|
|
907
|
-
*/
|
|
908
|
-
withQueryParam(key, value) {
|
|
909
|
-
if (value === null || value === undefined)
|
|
910
|
-
return this;
|
|
911
|
-
if (Array.isArray(value))
|
|
912
|
-
value.forEach(v => this.queryParams.append(key, String(v)));
|
|
913
|
-
else
|
|
914
|
-
this.queryParams.append(key, String(value));
|
|
915
|
-
return this;
|
|
916
|
-
}
|
|
917
|
-
/**
|
|
918
|
-
* Sets the mode of the request
|
|
919
|
-
* @param mode - The mode for the request (cors, no-cors, same-origin, navigate)
|
|
920
|
-
* @returns The instance for chaining
|
|
921
|
-
*/
|
|
922
|
-
withMode(mode) {
|
|
923
|
-
this.requestOptions.mode = mode;
|
|
924
|
-
return this;
|
|
925
|
-
}
|
|
926
|
-
/**
|
|
927
|
-
* Shorthand for setting the Content-Type header
|
|
928
|
-
* @param contentType - The content type
|
|
929
|
-
* @returns The instance for chaining
|
|
930
|
-
*/
|
|
931
|
-
withContentType(contentType) {
|
|
932
|
-
return this.withHeaders({
|
|
933
|
-
"Content-Type": contentType,
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
|
-
/**
|
|
937
|
-
* Shorthand for setting the Authorization header
|
|
938
|
-
* @param authValue - The authorization value
|
|
939
|
-
* @returns The instance for chaining
|
|
940
|
-
*/
|
|
941
|
-
withAuthorization(authValue) {
|
|
942
|
-
return this.withHeaders({
|
|
943
|
-
Authorization: authValue,
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
/**
|
|
947
|
-
* Shorthand for setting up Basic authentication
|
|
948
|
-
* @param username - The username
|
|
949
|
-
* @param password - The password
|
|
950
|
-
* @returns The instance for chaining
|
|
951
|
-
*/
|
|
952
|
-
withBasicAuth(username, password) {
|
|
953
|
-
const credentials = this.encodeBase64(`${username}:${password}`);
|
|
954
|
-
return this.withAuthorization(`Basic ${credentials}`);
|
|
955
|
-
}
|
|
956
|
-
/**
|
|
957
|
-
* Cross-environment base64 encoding
|
|
958
|
-
* Works in both browser and Node.js environments
|
|
959
|
-
*/
|
|
960
|
-
encodeBase64(str) {
|
|
961
|
-
// Modern approach using TextEncoder (available in both modern browsers and Node.js)
|
|
962
|
-
if (typeof TextEncoder !== "undefined" && typeof btoa === "function") {
|
|
963
|
-
const encoder = new TextEncoder();
|
|
964
|
-
const bytes = encoder.encode(str);
|
|
965
|
-
return btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)]));
|
|
966
|
-
}
|
|
967
|
-
// Browser environment
|
|
968
|
-
if (typeof btoa === "function")
|
|
969
|
-
return btoa(str);
|
|
970
|
-
// Node.js environment
|
|
971
|
-
if (typeof Buffer !== "undefined")
|
|
972
|
-
return Buffer.from(str).toString("base64");
|
|
973
|
-
// Fallback (should never happen in modern environments)
|
|
974
|
-
throw new Error("Base64 encoding is not supported in this environment");
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Sets the bearer token for authentication
|
|
978
|
-
* @param token - The bearer token
|
|
979
|
-
* @returns The instance for chaining
|
|
980
|
-
*/
|
|
981
|
-
withBearerToken(token) {
|
|
982
|
-
return this.withAuthorization(`Bearer ${token}`);
|
|
983
|
-
}
|
|
984
|
-
/**
|
|
985
|
-
* Helper function to check for header presence in a case-insensitive way
|
|
986
|
-
* @param headers Headers object
|
|
987
|
-
* @param headerName Header name to check
|
|
988
|
-
* @returns Boolean indicating if the header exists (case-insensitive)
|
|
989
|
-
*/
|
|
990
|
-
hasHeader(headers, headerName) {
|
|
991
|
-
return Object.keys(headers).some(key => key.toLowerCase() === headerName.toLowerCase());
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
|
-
* Sets cookies for the request
|
|
995
|
-
* @param cookies Object containing cookie name-value pairs or cookie options
|
|
996
|
-
* @returns The instance for chaining
|
|
997
|
-
*/
|
|
998
|
-
withCookies(cookies) {
|
|
999
|
-
const cookieEntries = Object.entries(cookies);
|
|
1000
|
-
if (cookieEntries.length === 0) {
|
|
1001
|
-
return this;
|
|
1002
|
-
}
|
|
1003
|
-
// Get current headers or initialize empty object
|
|
1004
|
-
const currentHeaders = this.requestOptions.headers || {};
|
|
1005
|
-
// Get all existing cookie headers with different cases
|
|
1006
|
-
const cookieValues = [];
|
|
1007
|
-
const cookieHeaderKeys = [];
|
|
1008
|
-
Object.keys(currentHeaders).forEach(key => {
|
|
1009
|
-
if (key.toLowerCase() === "cookie") {
|
|
1010
|
-
// Don't add empty cookie values
|
|
1011
|
-
if (currentHeaders[key])
|
|
1012
|
-
cookieValues.push(currentHeaders[key]);
|
|
1013
|
-
cookieHeaderKeys.push(key);
|
|
1014
|
-
}
|
|
1015
|
-
});
|
|
1016
|
-
// Format the new cookies
|
|
1017
|
-
const cookieString = CookieUtils.formatRequestCookies(cookies);
|
|
1018
|
-
// Choose which header name to use - preserve existing case if possible
|
|
1019
|
-
const headerName = cookieHeaderKeys.length > 0 ? cookieHeaderKeys[0] : "Cookie";
|
|
1020
|
-
// Combine all existing cookie values with the new ones
|
|
1021
|
-
const combinedCookieValue = [...cookieValues, cookieString].filter(Boolean).join("; ");
|
|
1022
|
-
// Create a new headers object without any cookie headers
|
|
1023
|
-
const newHeaders = { ...currentHeaders };
|
|
1024
|
-
cookieHeaderKeys.forEach(key => {
|
|
1025
|
-
delete newHeaders[key];
|
|
1026
|
-
});
|
|
1027
|
-
// Set the new combined cookie header
|
|
1028
|
-
this.requestOptions.headers = {
|
|
1029
|
-
...newHeaders,
|
|
1030
|
-
[headerName]: combinedCookieValue,
|
|
1031
|
-
};
|
|
1032
|
-
return this;
|
|
1033
|
-
}
|
|
1034
|
-
/**
|
|
1035
|
-
* Set a single cookie
|
|
1036
|
-
* @param name Cookie name
|
|
1037
|
-
* @param value Cookie value or options object
|
|
1038
|
-
* @returns The instance for chaining
|
|
1039
|
-
*/
|
|
1040
|
-
withCookie(name, value) {
|
|
1041
|
-
return this.withCookies({ [name]: value });
|
|
1042
|
-
}
|
|
1043
|
-
/**
|
|
1044
|
-
* Sets a CSRF token in the request headers
|
|
1045
|
-
* @param token The CSRF token
|
|
1046
|
-
* @param headerName The name of the header to use (default: X-CSRF-Token)
|
|
1047
|
-
* @returns The instance for chaining
|
|
1048
|
-
*/
|
|
1049
|
-
withCsrfToken(token, headerName = "X-CSRF-Token") {
|
|
1050
|
-
return this.withHeaders({
|
|
1051
|
-
[headerName]: token,
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
/**
|
|
1055
|
-
* Disables automatic anti-CSRF protection.
|
|
1056
|
-
* By default, X-Requested-With: XMLHttpRequest header is sent with all requests.
|
|
1057
|
-
* @returns The instance for chaining
|
|
1058
|
-
*/
|
|
1059
|
-
withoutCsrfProtection() {
|
|
1060
|
-
this.autoApplyCsrfProtection = false;
|
|
1061
|
-
return this;
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Sets common security headers to help prevent CSRF attacks
|
|
1065
|
-
* @returns The instance for chaining
|
|
1066
|
-
*/
|
|
1067
|
-
withAntiCsrfHeaders() {
|
|
1068
|
-
return this.withHeaders({
|
|
1069
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
/**
|
|
1073
|
-
* Enables caching for the request with the specified options
|
|
1074
|
-
* @param options The cache configuration options
|
|
1075
|
-
* @returns The instance for chaining
|
|
1076
|
-
*/
|
|
1077
|
-
withCache(options = {}) {
|
|
1078
|
-
this.cacheEnabled = true;
|
|
1079
|
-
this.cacheManager = new CacheManager(options);
|
|
1080
|
-
return this;
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Send the request to the specified URL
|
|
1084
|
-
* @param url The URL to send the request to
|
|
1085
|
-
*/
|
|
1086
|
-
sendTo(url) {
|
|
1087
|
-
// Format the URL with query parameters
|
|
1088
|
-
url = this.formatUrlWithQueryParams(url);
|
|
1089
|
-
// Apply automatic CSRF protection if enabled
|
|
1090
|
-
if (this.autoApplyCsrfProtection) {
|
|
1091
|
-
const config = Config.getInstance();
|
|
1092
|
-
// Apply anti-CSRF headers if enabled
|
|
1093
|
-
if (config.isAntiCsrfEnabled()) {
|
|
1094
|
-
this.withHeaders({
|
|
1095
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
1096
|
-
});
|
|
1097
|
-
}
|
|
1098
|
-
// Apply global CSRF token if set
|
|
1099
|
-
const globalToken = config.getCsrfToken();
|
|
1100
|
-
if (globalToken) {
|
|
1101
|
-
// Check if local token exists using case-insensitive comparison
|
|
1102
|
-
const headers = this.requestOptions.headers;
|
|
1103
|
-
const csrfHeaderName = config.getCsrfHeaderName();
|
|
1104
|
-
const hasLocalToken = this.hasHeader(headers, "X-CSRF-Token") || this.hasHeader(headers, csrfHeaderName);
|
|
1105
|
-
if (!hasLocalToken) {
|
|
1106
|
-
this.withHeaders({
|
|
1107
|
-
[csrfHeaderName]: globalToken,
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
// check for XSRF token in cookies and send it as a header
|
|
1112
|
-
if (config.isAutoXsrfEnabled() && typeof document !== "undefined") {
|
|
1113
|
-
const xsrfToken = CsrfUtils.getTokenFromCookie(config.getXsrfCookieName());
|
|
1114
|
-
if (xsrfToken && CsrfUtils.isValidToken(xsrfToken)) {
|
|
1115
|
-
const headers = this.requestOptions.headers;
|
|
1116
|
-
const xsrfHeaderName = config.getXsrfHeaderName();
|
|
1117
|
-
const hasLocalToken = this.hasHeader(headers, "X-XSRF-TOKEN") || this.hasHeader(headers, xsrfHeaderName);
|
|
1118
|
-
if (!hasLocalToken) {
|
|
1119
|
-
this.withHeaders({
|
|
1120
|
-
[xsrfHeaderName]: xsrfToken,
|
|
1121
|
-
});
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
// Cast the request options to RequestInit to ensure compatibility
|
|
1127
|
-
const fetchOptions = {
|
|
1128
|
-
...this.requestOptions,
|
|
1129
|
-
method: this.method,
|
|
1130
|
-
};
|
|
1131
|
-
// Create the base promise - handle caching if enabled
|
|
1132
|
-
let basePromise;
|
|
1133
|
-
if (this.cacheEnabled && this.cacheManager) {
|
|
1134
|
-
const cacheKey = this.cacheManager.generateKey(url, this.method, this.requestOptions.headers, this.requestOptions.body);
|
|
1135
|
-
basePromise = this.executeWithCache(url, fetchOptions, cacheKey);
|
|
1136
|
-
}
|
|
1137
|
-
else {
|
|
1138
|
-
basePromise = !this.requestOptions.retries ? this.executeRequest(url, fetchOptions) : this.executeWithRetries(url, fetchOptions);
|
|
1139
|
-
}
|
|
1140
|
-
const responsePromise = basePromise;
|
|
1141
|
-
// Attach convenience methods that begin processing immediately
|
|
1142
|
-
responsePromise.getJson = async () => {
|
|
1143
|
-
return basePromise.then(response => response.getJson());
|
|
1144
|
-
};
|
|
1145
|
-
responsePromise.getText = async () => {
|
|
1146
|
-
return basePromise.then(response => response.getText());
|
|
1147
|
-
};
|
|
1148
|
-
responsePromise.getBlob = async () => {
|
|
1149
|
-
return basePromise.then(response => response.getBlob());
|
|
1150
|
-
};
|
|
1151
|
-
responsePromise.getArrayBuffer = async () => {
|
|
1152
|
-
return basePromise.then(response => response.getArrayBuffer());
|
|
1153
|
-
};
|
|
1154
|
-
responsePromise.getBody = async () => {
|
|
1155
|
-
return basePromise.then(response => response.getBody());
|
|
1156
|
-
};
|
|
1157
|
-
responsePromise.getData = async (selector) => {
|
|
1158
|
-
try {
|
|
1159
|
-
const data = await basePromise.then(response => response.getJson());
|
|
1160
|
-
// If no selector is provided, return the raw JSON data
|
|
1161
|
-
if (!selector)
|
|
1162
|
-
return data;
|
|
1163
|
-
// Apply the selector if provided
|
|
1164
|
-
return selector(data);
|
|
1165
|
-
}
|
|
1166
|
-
catch (error) {
|
|
1167
|
-
if (error instanceof Error) {
|
|
1168
|
-
// If selector fails, enhance the error with the original data
|
|
1169
|
-
const enhancedError = new Error(`Data selector failed: ${error.message}. Original data: ${JSON.stringify(await basePromise.then(response => response.getJson()), null, 2)}`);
|
|
1170
|
-
enhancedError.stack = error.stack;
|
|
1171
|
-
throw enhancedError;
|
|
1172
|
-
}
|
|
1173
|
-
throw error;
|
|
1174
|
-
}
|
|
1175
|
-
};
|
|
1176
|
-
return responsePromise;
|
|
1177
|
-
}
|
|
1178
|
-
/**
|
|
1179
|
-
* Formats the URL with any query parameters
|
|
1180
|
-
* @param url The base URL
|
|
1181
|
-
* @returns The URL with query parameters appended
|
|
1182
|
-
*/
|
|
1183
|
-
formatUrlWithQueryParams(url) {
|
|
1184
|
-
if (!this.queryParams.toString()) {
|
|
1185
|
-
return url;
|
|
1186
|
-
}
|
|
1187
|
-
try {
|
|
1188
|
-
// Try to use the URL constructor (works for absolute URLs)
|
|
1189
|
-
const urlObj = new URL(url);
|
|
1190
|
-
// Merge our query params with any that might be in the URL already
|
|
1191
|
-
this.queryParams.forEach((value, key) => {
|
|
1192
|
-
urlObj.searchParams.append(key, value);
|
|
1193
|
-
});
|
|
1194
|
-
return urlObj.toString();
|
|
1195
|
-
}
|
|
1196
|
-
catch (error) {
|
|
1197
|
-
// Handle relative URLs
|
|
1198
|
-
const hasExistingParams = url.includes("?");
|
|
1199
|
-
const separator = hasExistingParams ? "&" : "?";
|
|
1200
|
-
return `${url}${separator}${this.queryParams.toString()}`;
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
/**
|
|
1204
|
-
* Executes a request with configured retry logic
|
|
1205
|
-
* @param url The formatted URL to send the request to
|
|
1206
|
-
* @param fetchOptions The fetch options to use
|
|
1207
|
-
* @returns A wrapped response object
|
|
1208
|
-
*/
|
|
1209
|
-
async executeWithRetries(url, fetchOptions) {
|
|
1210
|
-
let attempt = 0;
|
|
1211
|
-
const maxRetries = this.requestOptions.retries || 0;
|
|
1212
|
-
do {
|
|
1213
|
-
try {
|
|
1214
|
-
return await this.executeRequest(url, fetchOptions);
|
|
1215
|
-
}
|
|
1216
|
-
catch (error) {
|
|
1217
|
-
const requestError = error instanceof RequestError ? error : RequestError.networkError(url, fetchOptions.method, error);
|
|
1218
|
-
// Don't retry on timeout errors or if we've reached the max retries
|
|
1219
|
-
if (attempt >= maxRetries || requestError.timeoutError)
|
|
1220
|
-
throw requestError;
|
|
1221
|
-
attempt++;
|
|
1222
|
-
if (this.requestOptions.onRetry) {
|
|
1223
|
-
await this.requestOptions.onRetry({ attempt, error: requestError });
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
} while (attempt <= maxRetries);
|
|
1227
|
-
// If we somehow get here, we throw an error to avoid undefined behavior
|
|
1228
|
-
throw new RequestError(`Max retries reached (${maxRetries}) but request still failed`, url, fetchOptions.method);
|
|
1229
|
-
}
|
|
1230
|
-
async executeRequest(url, fetchOptions) {
|
|
1231
|
-
// Create a request controller for timeout if needed, or use the provided one
|
|
1232
|
-
const controller = this.abortController || new AbortController();
|
|
1233
|
-
const { signal } = controller;
|
|
1234
|
-
// Use the controller signal for the fetch request, but don't overwrite an existing signal
|
|
1235
|
-
fetchOptions.signal = fetchOptions.signal || signal;
|
|
1236
|
-
let timeoutId;
|
|
1237
|
-
// Flag to track if abort was caused by our timeout
|
|
1238
|
-
let abortedByTimeout = false;
|
|
1239
|
-
// Handle timeout regardless of whether an external controller is provided
|
|
1240
|
-
if (this.requestOptions.timeout) {
|
|
1241
|
-
if (this.abortController) {
|
|
1242
|
-
// If we have an external controller, create a separate timeout controller
|
|
1243
|
-
// that won't interfere with the external one
|
|
1244
|
-
// Use AbortSignal.timeout if available (modern browsers)
|
|
1245
|
-
if (typeof AbortSignal.timeout === "function") {
|
|
1246
|
-
const timeoutSignal = AbortSignal.timeout(this.requestOptions.timeout);
|
|
1247
|
-
// Add event listener to propagate the abort to the main controller
|
|
1248
|
-
timeoutSignal.addEventListener("abort", () => {
|
|
1249
|
-
if (!controller.signal.aborted) {
|
|
1250
|
-
abortedByTimeout = true;
|
|
1251
|
-
controller.abort();
|
|
1252
|
-
}
|
|
1253
|
-
});
|
|
1254
|
-
}
|
|
1255
|
-
else {
|
|
1256
|
-
// Fallback for browsers without AbortSignal.timeout
|
|
1257
|
-
timeoutId = setTimeout(() => {
|
|
1258
|
-
if (!controller.signal.aborted) {
|
|
1259
|
-
abortedByTimeout = true;
|
|
1260
|
-
controller.abort();
|
|
1261
|
-
}
|
|
1262
|
-
}, this.requestOptions.timeout);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
else {
|
|
1266
|
-
timeoutId = setTimeout(() => {
|
|
1267
|
-
abortedByTimeout = true;
|
|
1268
|
-
controller.abort();
|
|
1269
|
-
}, this.requestOptions.timeout);
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
try {
|
|
1273
|
-
let response;
|
|
1274
|
-
try {
|
|
1275
|
-
response = await fetch(url, fetchOptions);
|
|
1276
|
-
}
|
|
1277
|
-
catch (error) {
|
|
1278
|
-
// Check if this is an abort error that was specifically caused by our timeout
|
|
1279
|
-
if (error instanceof DOMException && error.name === "AbortError" && abortedByTimeout) {
|
|
1280
|
-
throw RequestError.timeout(url, fetchOptions.method, this.requestOptions.timeout);
|
|
1281
|
-
}
|
|
1282
|
-
// Otherwise it's either a user-triggered abort or another network error
|
|
1283
|
-
throw RequestError.networkError(url, fetchOptions.method, error);
|
|
1284
|
-
}
|
|
1285
|
-
if (!response.ok) {
|
|
1286
|
-
throw RequestError.fromResponse(response, url, fetchOptions.method);
|
|
1287
|
-
}
|
|
1288
|
-
// Return a wrapped response instead of parsing it
|
|
1289
|
-
return new ResponseWrapper(response);
|
|
1290
|
-
}
|
|
1291
|
-
finally {
|
|
1292
|
-
if (timeoutId) {
|
|
1293
|
-
clearTimeout(timeoutId);
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
/**
|
|
1298
|
-
* Executes a request using cache when possible
|
|
1299
|
-
* @param url The formatted URL to send the request to
|
|
1300
|
-
* @param fetchOptions The fetch options to use
|
|
1301
|
-
* @param cacheKey The cache key for this request
|
|
1302
|
-
*/
|
|
1303
|
-
async executeWithCache(url, fetchOptions, cacheKey) {
|
|
1304
|
-
// Try to get from cache first
|
|
1305
|
-
if (this.cacheManager) {
|
|
1306
|
-
const cachedEntry = await this.cacheManager.get(cacheKey);
|
|
1307
|
-
if (cachedEntry) {
|
|
1308
|
-
// Create a synthetic Response object from cached data
|
|
1309
|
-
const cachedResponse = new Response(JSON.stringify(cachedEntry.value), {
|
|
1310
|
-
status: 200,
|
|
1311
|
-
headers: new Headers(cachedEntry.headers || {}),
|
|
1312
|
-
});
|
|
1313
|
-
return new ResponseWrapper(cachedResponse);
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
// Not in cache, make the actual request
|
|
1317
|
-
const response = await (!this.requestOptions.retries ? this.executeRequest(url, fetchOptions) : this.executeWithRetries(url, fetchOptions));
|
|
1318
|
-
// Cache the successful response if caching is enabled
|
|
1319
|
-
// Only cache successful responses
|
|
1320
|
-
if (response.ok && this.cacheManager) {
|
|
1321
|
-
// Clone the response so we don't consume it
|
|
1322
|
-
try {
|
|
1323
|
-
const responseData = await response.getJson();
|
|
1324
|
-
// Extract headers to store in cache
|
|
1325
|
-
const headers = {};
|
|
1326
|
-
response.headers.forEach((value, key) => {
|
|
1327
|
-
headers[key] = value;
|
|
1328
|
-
});
|
|
1329
|
-
// Store in cache
|
|
1330
|
-
await this.cacheManager.set(cacheKey, responseData, headers);
|
|
1331
|
-
}
|
|
1332
|
-
catch (error) {
|
|
1333
|
-
// Failed to cache, but we can still return the response
|
|
1334
|
-
console.warn("Failed to cache response:", error);
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
return response;
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
/**
|
|
1342
|
-
* Base class for requests that can have a body (POST, PUT, PATCH)
|
|
1343
|
-
*/
|
|
1344
|
-
class BodyRequest extends BaseRequest {
|
|
1345
|
-
/**
|
|
1346
|
-
* Sets the body of the request
|
|
1347
|
-
* @param body - The request body
|
|
1348
|
-
*/
|
|
1349
|
-
withBody(body) {
|
|
1350
|
-
this.body = body;
|
|
1351
|
-
// Set body type and validate
|
|
1352
|
-
if (typeof body === "string") {
|
|
1353
|
-
this.bodyType = BodyType.STRING;
|
|
1354
|
-
this.setContentTypeIfNeeded("text/plain");
|
|
1355
|
-
}
|
|
1356
|
-
else if (body !== null &&
|
|
1357
|
-
typeof body === "object" &&
|
|
1358
|
-
!(body instanceof FormData || body instanceof Blob || body instanceof ArrayBuffer || body instanceof URLSearchParams || body instanceof ReadableStream)) {
|
|
1359
|
-
this.bodyType = BodyType.JSON;
|
|
1360
|
-
this.setContentTypeIfNeeded("application/json");
|
|
1361
|
-
// Validate JSON is stringifiable early
|
|
1362
|
-
try {
|
|
1363
|
-
JSON.stringify(body);
|
|
1364
|
-
}
|
|
1365
|
-
catch (error) {
|
|
1366
|
-
throw new Error(`Failed to stringify request body: ${String(error)}`);
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
else {
|
|
1370
|
-
this.bodyType = BodyType.BINARY;
|
|
1371
|
-
}
|
|
1372
|
-
return this;
|
|
1373
|
-
}
|
|
1374
|
-
setContentTypeIfNeeded(contentType) {
|
|
1375
|
-
// Only set if not already set
|
|
1376
|
-
const headers = this.requestOptions.headers;
|
|
1377
|
-
if (!Object.keys(headers).some(header => header.toLowerCase() === "content-type")) {
|
|
1378
|
-
this.withContentType(contentType);
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
/**
|
|
1382
|
-
* Send the request to the specified URL
|
|
1383
|
-
* Overrides the base implementation to add body handling
|
|
1384
|
-
*/
|
|
1385
|
-
sendTo(url) {
|
|
1386
|
-
if (this.body !== undefined) {
|
|
1387
|
-
// Remove previous body if it exists
|
|
1388
|
-
if (this.requestOptions.body)
|
|
1389
|
-
delete this.requestOptions.body;
|
|
1390
|
-
// Process the body based on its type
|
|
1391
|
-
if (this.bodyType === BodyType.JSON) {
|
|
1392
|
-
this.requestOptions.body = JSON.stringify(this.body);
|
|
1393
|
-
}
|
|
1394
|
-
else {
|
|
1395
|
-
this.requestOptions.body = this.body;
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
return super.sendTo(url);
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
// Methods without body
|
|
1403
|
-
class GetRequest extends BaseRequest {
|
|
1404
|
-
constructor() {
|
|
1405
|
-
super(...arguments);
|
|
1406
|
-
this.method = exports.HttpMethod.GET;
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
class HeadRequest extends BaseRequest {
|
|
1410
|
-
constructor() {
|
|
1411
|
-
super(...arguments);
|
|
1412
|
-
this.method = exports.HttpMethod.HEAD;
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
class OptionsRequest extends BaseRequest {
|
|
1416
|
-
constructor() {
|
|
1417
|
-
super(...arguments);
|
|
1418
|
-
this.method = exports.HttpMethod.OPTIONS;
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
class DeleteRequest extends BaseRequest {
|
|
1422
|
-
constructor() {
|
|
1423
|
-
super(...arguments);
|
|
1424
|
-
this.method = exports.HttpMethod.DELETE;
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
// Methods with body
|
|
1428
|
-
class PostRequest extends BodyRequest {
|
|
1429
|
-
constructor() {
|
|
1430
|
-
super(...arguments);
|
|
1431
|
-
this.method = exports.HttpMethod.POST;
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
class PutRequest extends BodyRequest {
|
|
1435
|
-
constructor() {
|
|
1436
|
-
super(...arguments);
|
|
1437
|
-
this.method = exports.HttpMethod.PUT;
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
class PatchRequest extends BodyRequest {
|
|
1441
|
-
constructor() {
|
|
1442
|
-
super(...arguments);
|
|
1443
|
-
this.method = exports.HttpMethod.PATCH;
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
/**
|
|
1448
|
-
* Create a GET request
|
|
1449
|
-
*/
|
|
1450
|
-
function get() {
|
|
1451
|
-
return new GetRequest();
|
|
1452
|
-
}
|
|
1453
|
-
/**
|
|
1454
|
-
* Create a POST request
|
|
1455
|
-
*/
|
|
1456
|
-
function post() {
|
|
1457
|
-
return new PostRequest();
|
|
1458
|
-
}
|
|
1459
|
-
/**
|
|
1460
|
-
* Create a PUT request
|
|
1461
|
-
*/
|
|
1462
|
-
function put() {
|
|
1463
|
-
return new PutRequest();
|
|
1464
|
-
}
|
|
1465
|
-
/**
|
|
1466
|
-
* Create a DELETE request
|
|
1467
|
-
* Note: We use 'del' to avoid JS keyword conflict
|
|
1468
|
-
*/
|
|
1469
|
-
function del() {
|
|
1470
|
-
return new DeleteRequest();
|
|
1471
|
-
}
|
|
1472
|
-
/**
|
|
1473
|
-
* Create a PATCH request
|
|
1474
|
-
*/
|
|
1475
|
-
function patch() {
|
|
1476
|
-
return new PatchRequest();
|
|
1477
|
-
}
|
|
1478
|
-
/**
|
|
1479
|
-
* Create a HEAD request
|
|
1480
|
-
*/
|
|
1481
|
-
function head() {
|
|
1482
|
-
return new HeadRequest();
|
|
1483
|
-
}
|
|
1484
|
-
/**
|
|
1485
|
-
* Create an OPTIONS request
|
|
1486
|
-
*/
|
|
1487
|
-
function options() {
|
|
1488
|
-
return new OptionsRequest();
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
// Create the main API object with configuration
|
|
1492
|
-
const create = {
|
|
1493
|
-
get,
|
|
1494
|
-
post,
|
|
1495
|
-
put,
|
|
1496
|
-
del,
|
|
1497
|
-
patch,
|
|
1498
|
-
head,
|
|
1499
|
-
options,
|
|
1500
|
-
// Add configuration access
|
|
1501
|
-
config: Config.getInstance(),
|
|
1502
|
-
};
|
|
1503
|
-
|
|
1504
|
-
exports.CacheManager = CacheManager;
|
|
1505
|
-
exports.CookieUtils = CookieUtils;
|
|
1506
|
-
exports.DeleteRequest = DeleteRequest;
|
|
1507
|
-
exports.GetRequest = GetRequest;
|
|
1508
|
-
exports.HeadRequest = HeadRequest;
|
|
1509
|
-
exports.OptionsRequest = OptionsRequest;
|
|
1510
|
-
exports.PatchRequest = PatchRequest;
|
|
1511
|
-
exports.PostRequest = PostRequest;
|
|
1512
|
-
exports.PutRequest = PutRequest;
|
|
1513
|
-
exports.RequestError = RequestError;
|
|
1514
|
-
exports.ResponseWrapper = ResponseWrapper;
|
|
1515
|
-
exports.createLocalStorageStorage = createLocalStorageStorage;
|
|
1516
|
-
exports.createMemoryStorage = createMemoryStorage;
|
|
1517
|
-
exports.createSessionStorageStorage = createSessionStorageStorage;
|
|
1518
|
-
exports["default"] = create;
|
|
1519
|
-
|
|
1520
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1521
|
-
|
|
1522
|
-
}));
|