create-request 0.0.1-beta.6 → 0.0.1-beta.8

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.
@@ -78,6 +78,20 @@ var BodyType;
78
78
  BodyType["STRING"] = "string";
79
79
  BodyType["BINARY"] = "binary";
80
80
  })(BodyType || (BodyType = {}));
81
+ /**
82
+ * Referrer policies for fetch requests
83
+ */
84
+ var ReferrerPolicy;
85
+ (function (ReferrerPolicy) {
86
+ ReferrerPolicy["ORIGIN"] = "origin";
87
+ ReferrerPolicy["UNSAFE_URL"] = "unsafe-url";
88
+ ReferrerPolicy["SAME_ORIGIN"] = "same-origin";
89
+ ReferrerPolicy["NO_REFERRER"] = "no-referrer";
90
+ ReferrerPolicy["STRICT_ORIGIN"] = "strict-origin";
91
+ ReferrerPolicy["ORIGIN_WHEN_CROSS_ORIGIN"] = "origin-when-cross-origin";
92
+ ReferrerPolicy["NO_REFERRER_WHEN_DOWNGRADE"] = "no-referrer-when-downgrade";
93
+ ReferrerPolicy["STRICT_ORIGIN_WHEN_CROSS_ORIGIN"] = "strict-origin-when-cross-origin";
94
+ })(ReferrerPolicy || (ReferrerPolicy = {}));
81
95
 
82
96
  class RequestError extends Error {
83
97
  constructor(message, url, method, options = {}) {
@@ -233,6 +247,331 @@ class ResponseWrapper {
233
247
  }
234
248
  }
235
249
 
250
+ /**
251
+ * Parse size strings like "10MB" into bytes
252
+ * @param size Size as number or string with units
253
+ */
254
+ function parseSize(size) {
255
+ var _a;
256
+ if (typeof size === "number")
257
+ return size;
258
+ const match = size.match(/^(\d+(?:\.\d+)?)\s*(KB|MB|GB)?$/i);
259
+ if (!match)
260
+ return 0;
261
+ const num = parseFloat(match[1]);
262
+ const unit = (_a = match[2]) === null || _a === void 0 ? void 0 : _a.toUpperCase();
263
+ switch (unit) {
264
+ case "KB":
265
+ return num * 1024;
266
+ case "MB":
267
+ return num * 1024 * 1024;
268
+ case "GB":
269
+ return num * 1024 * 1024 * 1024;
270
+ default:
271
+ return num;
272
+ }
273
+ }
274
+ /**
275
+ * Create a memory storage provider using Map
276
+ */
277
+ function createMemoryStorage() {
278
+ const store = new Map();
279
+ return {
280
+ get(key) {
281
+ return store.get(key) || null;
282
+ },
283
+ set(key, value) {
284
+ store.set(key, value);
285
+ },
286
+ has(key) {
287
+ return store.has(key);
288
+ },
289
+ delete(key) {
290
+ store.delete(key);
291
+ },
292
+ clear() {
293
+ store.clear();
294
+ },
295
+ };
296
+ }
297
+ /**
298
+ * Create a localStorage-based storage provider
299
+ */
300
+ function createLocalStorageStorage() {
301
+ return {
302
+ get(key) {
303
+ if (typeof localStorage === "undefined")
304
+ return null;
305
+ return localStorage.getItem(key);
306
+ },
307
+ set(key, value) {
308
+ if (typeof localStorage === "undefined")
309
+ return;
310
+ try {
311
+ localStorage.setItem(key, value);
312
+ }
313
+ catch (e) {
314
+ // Handle quota exceeded errors
315
+ console.warn("Cache storage quota exceeded, clearing cache");
316
+ void this.clear();
317
+ try {
318
+ localStorage.setItem(key, value);
319
+ }
320
+ catch (e) {
321
+ console.error("Failed to set cache item even after clearing cache");
322
+ }
323
+ }
324
+ },
325
+ has(key) {
326
+ if (typeof localStorage === "undefined")
327
+ return false;
328
+ return localStorage.getItem(key) !== null;
329
+ },
330
+ delete(key) {
331
+ if (typeof localStorage === "undefined")
332
+ return;
333
+ localStorage.removeItem(key);
334
+ },
335
+ clear() {
336
+ if (typeof localStorage === "undefined")
337
+ return;
338
+ localStorage.clear();
339
+ },
340
+ };
341
+ }
342
+ /**
343
+ * Create a sessionStorage-based storage provider
344
+ */
345
+ function createSessionStorageStorage() {
346
+ return {
347
+ get(key) {
348
+ if (typeof sessionStorage === "undefined")
349
+ return null;
350
+ return sessionStorage.getItem(key);
351
+ },
352
+ set(key, value) {
353
+ if (typeof sessionStorage === "undefined")
354
+ return;
355
+ try {
356
+ sessionStorage.setItem(key, value);
357
+ }
358
+ catch (e) {
359
+ // Handle quota exceeded errors
360
+ console.warn("Cache storage quota exceeded, clearing cache");
361
+ void this.clear();
362
+ try {
363
+ sessionStorage.setItem(key, value);
364
+ }
365
+ catch (e) {
366
+ console.error("Failed to set cache item even after clearing cache");
367
+ }
368
+ }
369
+ },
370
+ has(key) {
371
+ if (typeof sessionStorage === "undefined")
372
+ return false;
373
+ return sessionStorage.getItem(key) !== null;
374
+ },
375
+ delete(key) {
376
+ if (typeof sessionStorage === "undefined")
377
+ return;
378
+ sessionStorage.removeItem(key);
379
+ },
380
+ clear() {
381
+ if (typeof sessionStorage === "undefined")
382
+ return;
383
+ sessionStorage.clear();
384
+ },
385
+ };
386
+ }
387
+ /**
388
+ * Create a storage provider from a cache options object
389
+ */
390
+ function createStorageProvider(options) {
391
+ const { storage } = options || {};
392
+ if (!storage) {
393
+ // Default to memory storage
394
+ return createMemoryStorage();
395
+ }
396
+ // Check if storage is already a StorageProvider
397
+ if (typeof storage.get === "function" && typeof storage.set === "function") {
398
+ return storage;
399
+ }
400
+ // Check if it's localStorage or sessionStorage
401
+ if (storage === localStorage) {
402
+ return createLocalStorageStorage();
403
+ }
404
+ if (storage === sessionStorage) {
405
+ return createSessionStorageStorage();
406
+ }
407
+ // Check if it's a Map
408
+ if (storage instanceof Map) {
409
+ return {
410
+ get(key) {
411
+ return storage.get(key) || null;
412
+ },
413
+ set(key, value, _ttl) {
414
+ storage.set(key, value);
415
+ },
416
+ has(key) {
417
+ return storage.has(key);
418
+ },
419
+ delete(key) {
420
+ storage.delete(key);
421
+ },
422
+ clear() {
423
+ storage.clear();
424
+ },
425
+ };
426
+ }
427
+ // Default to memory storage
428
+ console.warn("Unsupported storage type, falling back to memory storage");
429
+ return createMemoryStorage();
430
+ }
431
+
432
+ /**
433
+ * Cache manager that handles storage operations
434
+ */
435
+ class CacheManager {
436
+ constructor(options = {}) {
437
+ this.entryCount = 0;
438
+ this.totalSize = 0;
439
+ this.options = options;
440
+ this.storage = createStorageProvider(options);
441
+ }
442
+ /**
443
+ * Generate a cache key based on request details
444
+ */
445
+ generateKey(url, method, headers, body) {
446
+ // Use custom key generator if provided
447
+ if (this.options.keyGenerator) {
448
+ const customKey = this.options.keyGenerator(url, method, headers, body);
449
+ return this.options.keyPrefix ? `${this.options.keyPrefix}:${customKey}` : customKey;
450
+ }
451
+ // Default key generation
452
+ let key = `${method}:${url}`;
453
+ // Add vary headers to key if specified
454
+ if (this.options.varyByHeaders && headers) {
455
+ const varyHeaders = {};
456
+ this.options.varyByHeaders.forEach(header => {
457
+ const normalizedHeader = header.toLowerCase();
458
+ // Find the header in case-insensitive way
459
+ Object.keys(headers).forEach(h => {
460
+ if (h.toLowerCase() === normalizedHeader) {
461
+ varyHeaders[normalizedHeader] = headers[h];
462
+ }
463
+ });
464
+ });
465
+ if (Object.keys(varyHeaders).length > 0) {
466
+ key += `:${JSON.stringify(varyHeaders)}`;
467
+ }
468
+ }
469
+ // Include body in cache key for non-GET requests if it's a simple value
470
+ if (method !== "GET" && body !== undefined && body !== null) {
471
+ try {
472
+ if (typeof body === "string" || typeof body === "number" || typeof body === "boolean") {
473
+ key += `:${String(body)}`;
474
+ }
475
+ else if (typeof body === "object") {
476
+ key += `:${JSON.stringify(body)}`;
477
+ }
478
+ }
479
+ catch (e) {
480
+ console.warn("Failed to stringify request body for cache key");
481
+ }
482
+ }
483
+ // Add prefix if specified
484
+ return this.options.keyPrefix ? `${this.options.keyPrefix}:${key}` : key;
485
+ }
486
+ /**
487
+ * Get a value from cache
488
+ */
489
+ async get(key) {
490
+ const value = await Promise.resolve(this.storage.get(key));
491
+ if (!value)
492
+ return null;
493
+ try {
494
+ const entry = JSON.parse(value);
495
+ // Check if the entry has expired
496
+ if (entry.expiry && entry.expiry < Date.now()) {
497
+ await Promise.resolve(this.storage.delete(key));
498
+ return null;
499
+ }
500
+ return entry;
501
+ }
502
+ catch (e) {
503
+ // If we can't parse the cached value, remove it
504
+ await Promise.resolve(this.storage.delete(key));
505
+ return null;
506
+ }
507
+ }
508
+ /**
509
+ * Set a value in cache
510
+ */
511
+ async set(key, value, headers) {
512
+ const now = Date.now();
513
+ const ttl = this.options.ttl;
514
+ const entry = {
515
+ value,
516
+ timestamp: now,
517
+ expiry: ttl ? now + ttl : null,
518
+ headers,
519
+ };
520
+ const serialized = JSON.stringify(entry);
521
+ // Check size constraints if applicable
522
+ if (this.options.maxSize) {
523
+ const maxBytes = parseSize(this.options.maxSize);
524
+ const entrySize = new TextEncoder().encode(serialized).length;
525
+ // If a single entry exceeds the max size, reject it without clearing the cache
526
+ if (entrySize > maxBytes) {
527
+ console.warn(`Cache entry size (${entrySize} bytes) exceeds maximum cache size (${maxBytes} bytes)`);
528
+ return;
529
+ }
530
+ // Only clear if adding this entry would exceed the total limit
531
+ if (this.totalSize + entrySize > maxBytes) {
532
+ // Simple strategy: clear everything
533
+ // A more sophisticated strategy would remove oldest entries first
534
+ await Promise.resolve(this.storage.clear());
535
+ this.totalSize = 0;
536
+ this.entryCount = 0;
537
+ }
538
+ this.totalSize += entrySize;
539
+ }
540
+ // Check max entries constraint
541
+ if (this.options.maxEntries) {
542
+ this.entryCount++;
543
+ if (this.entryCount > this.options.maxEntries) {
544
+ // Simple strategy: clear everything
545
+ // A more sophisticated strategy would use LRU cache
546
+ await Promise.resolve(this.storage.clear());
547
+ this.entryCount = 1;
548
+ }
549
+ }
550
+ // Pass ttl to storage provider if available
551
+ await Promise.resolve(this.storage.set(key, serialized, ttl));
552
+ }
553
+ /**
554
+ * Check if a key exists in the cache
555
+ */
556
+ async has(key) {
557
+ return Promise.resolve(this.storage.has(key));
558
+ }
559
+ /**
560
+ * Delete a key from the cache
561
+ */
562
+ async delete(key) {
563
+ return Promise.resolve(this.storage.delete(key));
564
+ }
565
+ /**
566
+ * Clear the entire cache
567
+ */
568
+ async clear() {
569
+ this.entryCount = 0;
570
+ this.totalSize = 0;
571
+ return Promise.resolve(this.storage.clear());
572
+ }
573
+ }
574
+
236
575
  /**
237
576
  * Global configuration for create-request
238
577
  */
@@ -446,6 +785,7 @@ class BaseRequest {
446
785
  };
447
786
  this.queryParams = new URLSearchParams();
448
787
  this.autoApplyCsrfProtection = true;
788
+ this.cacheEnabled = false;
449
789
  }
450
790
  withHeaders(headers) {
451
791
  this.requestOptions.headers = {
@@ -497,6 +837,15 @@ class BaseRequest {
497
837
  this.requestOptions.referrer = referrer;
498
838
  return this;
499
839
  }
840
+ /**
841
+ * Sets the referrer policy for the request
842
+ * @param policy - The referrer policy to use
843
+ * @returns The instance for chaining
844
+ */
845
+ withReferrerPolicy(policy) {
846
+ this.requestOptions.referrerPolicy = policy;
847
+ return this;
848
+ }
500
849
  /**
501
850
  * Sets the redirect policy for the request
502
851
  * @param redirect - Can be 'follow', 'error', or 'manual'
@@ -603,6 +952,12 @@ class BaseRequest {
603
952
  * Works in both browser and Node.js environments
604
953
  */
605
954
  encodeBase64(str) {
955
+ // Modern approach using TextEncoder (available in both modern browsers and Node.js)
956
+ if (typeof TextEncoder !== "undefined" && typeof btoa === "function") {
957
+ const encoder = new TextEncoder();
958
+ const bytes = encoder.encode(str);
959
+ return btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)]));
960
+ }
606
961
  // Browser environment
607
962
  if (typeof btoa === "function")
608
963
  return btoa(str);
@@ -708,6 +1063,16 @@ class BaseRequest {
708
1063
  "X-Requested-With": "XMLHttpRequest",
709
1064
  });
710
1065
  }
1066
+ /**
1067
+ * Enables caching for the request with the specified options
1068
+ * @param options The cache configuration options
1069
+ * @returns The instance for chaining
1070
+ */
1071
+ withCache(options = {}) {
1072
+ this.cacheEnabled = true;
1073
+ this.cacheManager = new CacheManager(options);
1074
+ return this;
1075
+ }
711
1076
  /**
712
1077
  * Send the request to the specified URL
713
1078
  * @param url The URL to send the request to
@@ -757,8 +1122,15 @@ class BaseRequest {
757
1122
  ...this.requestOptions,
758
1123
  method: this.method,
759
1124
  };
760
- // Create the base promise
761
- const basePromise = !this.requestOptions.retries ? this.executeRequest(url, fetchOptions) : this.executeWithRetries(url, fetchOptions);
1125
+ // Create the base promise - handle caching if enabled
1126
+ let basePromise;
1127
+ if (this.cacheEnabled && this.cacheManager) {
1128
+ const cacheKey = this.cacheManager.generateKey(url, this.method, this.requestOptions.headers, this.requestOptions.body);
1129
+ basePromise = this.executeWithCache(url, fetchOptions, cacheKey);
1130
+ }
1131
+ else {
1132
+ basePromise = !this.requestOptions.retries ? this.executeRequest(url, fetchOptions) : this.executeWithRetries(url, fetchOptions);
1133
+ }
762
1134
  const responsePromise = basePromise;
763
1135
  // Attach convenience methods that begin processing immediately
764
1136
  responsePromise.getJson = async () => {
@@ -776,6 +1148,25 @@ class BaseRequest {
776
1148
  responsePromise.getBody = async () => {
777
1149
  return basePromise.then(response => response.getBody());
778
1150
  };
1151
+ responsePromise.getData = async (selector) => {
1152
+ try {
1153
+ const data = await basePromise.then(response => response.getJson());
1154
+ // If no selector is provided, return the raw JSON data
1155
+ if (!selector)
1156
+ return data;
1157
+ // Apply the selector if provided
1158
+ return selector(data);
1159
+ }
1160
+ catch (error) {
1161
+ if (error instanceof Error) {
1162
+ // If selector fails, enhance the error with the original data
1163
+ const enhancedError = new Error(`Data selector failed: ${error.message}. Original data: ${JSON.stringify(await basePromise.then(response => response.getJson()), null, 2)}`);
1164
+ enhancedError.stack = error.stack;
1165
+ throw enhancedError;
1166
+ }
1167
+ throw error;
1168
+ }
1169
+ };
779
1170
  return responsePromise;
780
1171
  }
781
1172
  /**
@@ -897,6 +1288,48 @@ class BaseRequest {
897
1288
  }
898
1289
  }
899
1290
  }
1291
+ /**
1292
+ * Executes a request using cache when possible
1293
+ * @param url The formatted URL to send the request to
1294
+ * @param fetchOptions The fetch options to use
1295
+ * @param cacheKey The cache key for this request
1296
+ */
1297
+ async executeWithCache(url, fetchOptions, cacheKey) {
1298
+ // Try to get from cache first
1299
+ if (this.cacheManager) {
1300
+ const cachedEntry = await this.cacheManager.get(cacheKey);
1301
+ if (cachedEntry) {
1302
+ // Create a synthetic Response object from cached data
1303
+ const cachedResponse = new Response(JSON.stringify(cachedEntry.value), {
1304
+ status: 200,
1305
+ headers: new Headers(cachedEntry.headers || {}),
1306
+ });
1307
+ return new ResponseWrapper(cachedResponse);
1308
+ }
1309
+ }
1310
+ // Not in cache, make the actual request
1311
+ const response = await (!this.requestOptions.retries ? this.executeRequest(url, fetchOptions) : this.executeWithRetries(url, fetchOptions));
1312
+ // Cache the successful response if caching is enabled
1313
+ // Only cache successful responses
1314
+ if (response.ok && this.cacheManager) {
1315
+ // Clone the response so we don't consume it
1316
+ try {
1317
+ const responseData = await response.getJson();
1318
+ // Extract headers to store in cache
1319
+ const headers = {};
1320
+ response.headers.forEach((value, key) => {
1321
+ headers[key] = value;
1322
+ });
1323
+ // Store in cache
1324
+ await this.cacheManager.set(cacheKey, responseData, headers);
1325
+ }
1326
+ catch (error) {
1327
+ // Failed to cache, but we can still return the response
1328
+ console.warn("Failed to cache response:", error);
1329
+ }
1330
+ }
1331
+ return response;
1332
+ }
900
1333
  }
901
1334
 
902
1335
  /**
@@ -1062,4 +1495,4 @@ const create = {
1062
1495
  config: Config.getInstance(),
1063
1496
  };
1064
1497
 
1065
- export { CacheMode, CookieUtils, CredentialsPolicy, DeleteRequest, GetRequest, HeadRequest, HttpMethod, OptionsRequest, PatchRequest, PostRequest, PutRequest, RedirectMode, RequestError, RequestMode, RequestPriority, ResponseWrapper, SameSitePolicy, create as default };
1498
+ export { CacheManager, CacheMode, CookieUtils, CredentialsPolicy, DeleteRequest, GetRequest, HeadRequest, HttpMethod, OptionsRequest, PatchRequest, PostRequest, PutRequest, RedirectMode, RequestError, RequestMode, RequestPriority, ResponseWrapper, SameSitePolicy, createLocalStorageStorage, createMemoryStorage, createSessionStorageStorage, create as default };