create-request 0.0.1-beta.7 → 0.0.1-beta.9

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 CHANGED
@@ -8,28 +8,58 @@
8
8
 
9
9
  ## Core Features
10
10
 
11
- - ⛓️ **Chainable API** - Build and execute requests with a fluent interface
12
11
  - ⏱️ **Timeout Support** - Set timeouts for any request
13
- - 🔁 **Automatic Retries** - Retry failed requests with customizable settings
14
- - 🔐 **Auth Helpers** - Simple methods for common authentication patterns
15
- - 📦 **Typed Responses** - Full TypeScript support for response data
12
+ - 🚀 **Performance** - Tiny bundle size with zero dependencies
16
13
  - 🚧 **Error Handling** - Detailed error info with custom error class
14
+ - 🔐 **Auth Helpers** - Simple methods for common authentication patterns
15
+ - ⛓️ **Chainable API** - Build and execute requests with a fluent interface
16
+ - 📉 **Reduced Boilerplate** - Write 60% less code for common API operations
17
+ - 🔁 **Automatic Retries** - Retry failed requests with customizable settings
18
+ - 🛡️ **Type Safety** - Full TypeScript support with intelligent type inference
17
19
  - 📝 **Response Caching** - Flexible caching system with multiple storage options
18
20
 
19
21
  ## Why create-request?
20
22
 
21
- **You've probably been here before:** writing boilerplate fetch code, handling retries manually, struggling with timeouts, or wrestling with TypeScript types for your API responses. `create-request` solves all of that with an elegant API that separates request building from execution:
23
+ **API interactions often require repetitive code patterns** - handling HTTP status checks, parsing responses, managing errors, and dealing with TypeScript types. `create-request` provides a clean, efficient solution with an elegant API that separates request building from execution:
22
24
 
23
25
  ```typescript
24
- // Create a request
25
- const usersRequest = create.get()
26
- .withTimeout(5000)
27
- .withRetries(3)
28
- .withBearerToken('your-token');
26
+ // Before: Regular fetch
27
+ async function createUser(userData) {
28
+ try {
29
+ const response = await fetch('https://api.example.com/users', {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ 'Authorization': 'Basic ' + btoa('username:password')
34
+ },
35
+ body: JSON.stringify(userData)
36
+ });
37
+
38
+ if (!response.ok) {
39
+ throw new Error(`HTTP error! status: ${response.status}`);
40
+ }
41
+ const data = await response.json();
42
+ return data;
43
+ } catch (error) {
44
+ console.error('Fetch error:', error);
45
+ throw error;
46
+ }
47
+ }
48
+
49
+ // After: With create-request
50
+ import create from 'create-request';
29
51
 
30
- // Execute the request
31
- const users = await usersRequest.sendTo('https://api.example.com/users')
32
- .getJson<User[]>();
52
+ function createUser(userData) {
53
+ return create.post()
54
+ .withBasicAuth('username', 'password')
55
+ .withBody(userData)
56
+ .sendTo('https://api.example.com/users')
57
+ .getData<User>()
58
+ .catch(error => {
59
+ console.error('Fetch error:', error);
60
+ throw error;
61
+ });
62
+ }
33
63
  ```
34
64
 
35
65
  ## Installation
@@ -44,67 +74,6 @@ Or with yarn:
44
74
  yarn add create-request
45
75
  ```
46
76
 
47
- ## 🔄 Fetch API Comparison
48
-
49
- ### Fetch API
50
-
51
- ```typescript
52
- // Basic GET request with fetch
53
- try {
54
- const response = await fetch('https://api.example.com/users');
55
- if (!response.ok) {
56
- throw new Error(`HTTP error ${response.status}`);
57
- }
58
- const users = await response.json();
59
- console.log(users);
60
- } catch (error) {
61
- console.error('Error fetching users:', error);
62
- }
63
-
64
- // POST request with fetch
65
- try {
66
- const response = await fetch('https://api.example.com/users', {
67
- method: 'POST',
68
- headers: {
69
- 'Content-Type': 'application/json',
70
- 'Authorization': 'Bearer token123'
71
- },
72
- body: JSON.stringify({ name: 'John Doe', email: 'john@example.com' })
73
- });
74
- if (!response.ok) {
75
- throw new Error(`HTTP error ${response.status}`);
76
- }
77
- const result = await response.json();
78
- console.log(result);
79
- } catch (error) {
80
- console.error('Error creating user:', error);
81
- }
82
- ```
83
-
84
- ### create-request API
85
-
86
- ```typescript
87
- // Basic GET request with create-request
88
- try {
89
- const users = await create.get().sendTo('https://api.example.com/users').getJson();
90
- console.log(users);
91
- } catch (error) {
92
- console.error('Error fetching users:', error.message);
93
- }
94
-
95
- // POST request with create-request
96
- const createUserRequest = create.post()
97
- .withBody({ name: 'John Doe', email: 'john@example.com' })
98
- .withBearerToken('token123');
99
-
100
- try {
101
- const result = await createUserRequest.sendTo('https://api.example.com/users').getJson();
102
- console.log(result);
103
- } catch (error) {
104
- console.error('Error creating user:', error.message);
105
- }
106
- ```
107
-
108
77
  ## Basic Usage
109
78
 
110
79
  ### Creating Requests
@@ -124,30 +93,75 @@ const optionsRequest = create.options();
124
93
 
125
94
  ### Request Configuration
126
95
 
96
+ The library provides a comprehensive set of configuration methods that can be chained together to customize your requests:
97
+
127
98
  ```typescript
128
- import { RequestPriority, CredentialsPolicy } from 'create-request';
99
+ import create, { RequestPriority, CredentialsPolicy, RequestMode, CacheMode, RedirectMode, ReferrerPolicy } from 'create-request';
129
100
 
130
101
  // Configure request options
131
102
  const request = create.get()
132
- // Headers
133
- .withHeaders({ 'X-API-Key': 'abc123' })
103
+ // Basic headers
104
+ .withHeaders({ 'X-API-Key': 'abc123', 'Accept-Language': 'en-US' })
105
+ .withHeader('Custom-Header', 'value') // Add a single header
134
106
 
135
- // Timeout and retries
136
- .withTimeout(5000)
137
- .withRetries(3)
107
+ // Timeout settings
108
+ .withTimeout(5000) // Request will abort after 5 seconds
109
+
110
+ // Automatic retry configuration
111
+ .withRetries(3) // Retry up to 3 times on failure
138
112
  .onRetry(({ attempt, error }) => {
139
- console.log(`Attempt ${attempt} failed: ${error.message}`);
113
+ console.log(`Attempt ${attempt} failed: ${error.message}. Retrying...`);
140
114
  })
141
115
 
142
- // Authentication
143
- .withBearerToken('your-token')
116
+ // Authentication methods
117
+ .withBearerToken('your-token') // Adds Authorization: Bearer your-token
118
+ .withBasicAuth('username', 'password') // HTTP Basic Authentication
119
+
120
+ // URL parameters
121
+ .withQueryParams({ search: 'term', page: 1, limit: 20 })
122
+ .withQueryParam('filter', 'active') // Add a single query parameter
144
123
 
145
- // Query parameters
146
- .withQueryParams({ search: 'term', page: 1 })
124
+ // Request body configuration (for POST/PUT/PATCH)
125
+ .withContentType('application/json') // Set specific content type
147
126
 
148
- // Advanced options
149
- .withCredentials(CredentialsPolicy.INCLUDE)
150
- .withPriority(RequestPriority.HIGH);
127
+ // Fetch API options
128
+ .withCredentials(CredentialsPolicy.INCLUDE) // Includes cookies with cross-origin requests
129
+ .withMode(RequestMode.CORS) // Controls CORS behavior
130
+ .withRedirect(RedirectMode.FOLLOW) // Controls redirect behavior (follow, error, manual)
131
+ .withReferrer('https://example.com') // Sets request referrer
132
+ .withReferrerPolicy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE) // Controls referrer policy
133
+ .withPriority(RequestPriority.HIGH) // Sets request priority
134
+ ```
135
+
136
+ Each configuration method returns the request object, allowing for a fluent interface where methods can be chained together. You can configure only what you need for a specific request:
137
+
138
+ ```typescript
139
+ // Simple example with just what's needed
140
+ const searchUsers = create.get()
141
+ .withBearerToken(userToken)
142
+ .withQueryParams({ q: searchTerm, limit: 20 })
143
+ .withTimeout(3000);
144
+
145
+ // Now execute the request
146
+ const users = await searchUsers.sendTo('https://api.example.com/users').getData();
147
+ ```
148
+
149
+ You can also create reusable base requests with common settings:
150
+
151
+ ```typescript
152
+ // Create base authenticated request
153
+ const apiBase = create.get()
154
+ .withHeaders({
155
+ 'X-API-Version': '1.2',
156
+ 'Accept-Language': 'en-US'
157
+ })
158
+ .withBearerToken(authToken)
159
+ .withTimeout(5000)
160
+ .withRetries(2);
161
+
162
+ // Use the base request for different endpoints
163
+ const users = await apiBase.sendTo('https://api.example.com/users').getData();
164
+ const products = await apiBase.sendTo('https://api.example.com/products').getData();
151
165
  ```
152
166
 
153
167
  ### Request Bodies (POST/PUT/PATCH)
@@ -182,6 +196,56 @@ const textData = await request.sendTo('https://api.example.com/endpoint').getTex
182
196
  const blobData = await request.sendTo('https://api.example.com/endpoint').getBlob();
183
197
  const bodyStream = await request.sendTo('https://api.example.com/endpoint').getBody();
184
198
  const arrayBuffer = await request.sendTo('https://api.example.com/endpoint').getArrayBuffer();
199
+
200
+ // Using the data selector API to extract specific data
201
+ const userData = await request.sendTo('https://api.example.com/users')
202
+ .getData(data => data.results.users);
203
+
204
+ // Using the data selector without a selector function just returns the full JSON response
205
+ const fullData = await request.sendTo('https://api.example.com/data').getData();
206
+ ```
207
+
208
+ ### Data Selection
209
+
210
+ The `getData` method provides a powerful way to extract and transform specific data from API responses:
211
+
212
+ ```typescript
213
+ // Extract specific properties from nested structures
214
+ const posts = await request
215
+ .sendTo('https://api.example.com/feed')
216
+ .getData(data => data.feed.posts);
217
+
218
+ // Transform data in the selector function
219
+ const usernames = await request
220
+ .sendTo('https://api.example.com/users')
221
+ .getData(data => data.users.map(user => user.username));
222
+
223
+ // Apply filtering in the selector
224
+ const activeUsers = await request
225
+ .sendTo('https://api.example.com/users')
226
+ .getData(data => data.users.filter(user => user.isActive));
227
+
228
+ // Combine data from complex nested structures
229
+ const combinedData = await request
230
+ .sendTo('https://api.example.com/dashboard')
231
+ .getData(data => ({
232
+ userCount: data.stats.users.total,
233
+ recentPosts: data.content.recent.slice(0, 5),
234
+ notifications: data.user.notifications.unread
235
+ }));
236
+ ```
237
+
238
+ When a selector fails, the error message will contain the original response data to help diagnose the issue:
239
+
240
+ ```typescript
241
+ try {
242
+ const result = await request
243
+ .sendTo('https://api.example.com/data')
244
+ .getData(data => data.results.items); // Will fail if structure is different
245
+ } catch (error) {
246
+ console.error(error);
247
+ // Error message includes the original response data
248
+ }
185
249
  ```
186
250
 
187
251
  ### Error Handling
@@ -285,12 +349,33 @@ interface User {
285
349
  email: string;
286
350
  }
287
351
 
352
+ interface ApiResponse {
353
+ users: User[];
354
+ pagination: {
355
+ total: number;
356
+ page: number;
357
+ }
358
+ }
359
+
288
360
  const userRequest = create.get();
289
- const user = await userRequest.sendTo('https://api.example.com/user/123')
290
- .getJson<User>();
291
361
 
292
- // TypeScript knows the type
293
- console.log(user.name);
362
+ // Type the full response
363
+ const response = await userRequest
364
+ .sendTo('https://api.example.com/users')
365
+ .getJson<ApiResponse>();
366
+
367
+ // Or use getData with type parameters
368
+ const users = await userRequest
369
+ .sendTo('https://api.example.com/users')
370
+ .getData<ApiResponse, User[]>(data => data.users);
371
+
372
+ // Or just get the full typed response
373
+ const fullData = await userRequest
374
+ .sendTo('https://api.example.com/users')
375
+ .getData<ApiResponse>();
376
+
377
+ // TypeScript knows the types
378
+ console.log(users[0].name);
294
379
  ```
295
380
 
296
381
  ### Promise Chaining
@@ -323,39 +408,11 @@ const request = create.get()
323
408
  keyGenerator: (url, method, headers) => {
324
409
  // Include user ID from authorization header in the cache key
325
410
  const authHeader = headers?.['Authorization'] || '';
326
- const userId = extractUserIdFromToken(authHeader);
327
- return `${method}:${url}:${userId}`;
411
+ const userMatch = authHeader.match(/User-(\d+)/);
412
+ const userId = userMatch ? userMatch[1] : 'anonymous';
413
+ return `${method}:${userId}:${url}`;
328
414
  }
329
- });
330
-
331
- // Simple custom storage provider example
332
- const customStorage: StorageProvider = {
333
- get(key: string): string | null {
334
- // Get from your custom storage
335
- return localStorage.getItem(`my-app:${key}`);
336
- },
337
- set(key: string, value: string): void {
338
- // Set in your custom storage
339
- localStorage.setItem(`my-app:${key}`, value);
340
- },
341
- has(key: string): boolean {
342
- // Check if exists in your custom storage
343
- return localStorage.getItem(`my-app:${key}`) !== null;
344
- },
345
- delete(key: string): void {
346
- // Remove from your custom storage
347
- localStorage.removeItem(`my-app:${key}`);
348
- },
349
- clear(): void {
350
- // Clear all items from your namespace
351
- Object.keys(localStorage)
352
- .filter(key => key.startsWith('my-app:'))
353
- .forEach(key => localStorage.removeItem(key));
354
- }
355
- };
356
-
357
- const request = create.get()
358
- .withCache({ storage: customStorage })
415
+ })
359
416
  .sendTo('https://api.example.com/data');
360
417
  ```
361
418
 
@@ -424,24 +481,54 @@ With custom storage providers, you can integrate with any storage system, such a
424
481
 
425
482
  ## CSRF Protection
426
483
 
484
+ Cross-Site Request Forgery (CSRF) is a type of security vulnerability where unauthorized commands are executed on behalf of an authenticated user. `create-request` provides built-in protection mechanisms to help prevent CSRF attacks.
485
+
486
+ ### How CSRF Protection Works
487
+
488
+ The library employs multiple strategies to protect against CSRF attacks:
489
+
490
+ 1. **Automatic X-Requested-With Header**: By default, all requests include the `X-Requested-With: XMLHttpRequest` header, which helps servers identify legitimate AJAX requests.
491
+
492
+ 2. **CSRF Token Support**: The library can automatically include CSRF tokens in request headers, which servers can validate to ensure the request came from your application.
493
+
494
+ 3. **XSRF Cookie Reading**: For frameworks that use the double-submit cookie pattern (like Laravel, Rails, or Django), the library can automatically read XSRF tokens from cookies and include them in request headers.
495
+
427
496
  ### Global CSRF Configuration
428
497
 
498
+ You can configure CSRF settings globally for all requests:
499
+
429
500
  ```typescript
430
501
  // Configure CSRF settings for all requests
431
502
  create.config.setCsrfToken('your-csrf-token');
432
- create.config.setCsrfHeaderName('X-CSRF-Token');
433
- create.config.setXsrfCookieName('XSRF-TOKEN');
503
+ create.config.setCsrfHeaderName('X-CSRF-Token'); // Default header name
504
+ create.config.setXsrfCookieName('XSRF-TOKEN'); // Default cookie name
505
+ create.config.setEnableAntiCsrf(true); // Enable/disable X-Requested-With header
506
+ create.config.setEnableAutoXsrf(true); // Enable/disable automatic cookie-to-header token
434
507
  ```
435
508
 
436
509
  ### Per-Request CSRF Settings
437
510
 
511
+ You can also configure CSRF protection on individual requests:
512
+
438
513
  ```typescript
439
514
  // Configure CSRF for a specific request
440
515
  const request = create.post()
441
- .withCsrfToken('request-specific-token')
442
- .withoutCsrfProtection(); // Disable automatic CSRF protection
516
+ .withCsrfToken('request-specific-token') // Set a specific token
517
+ .withAntiCsrfHeaders() // Explicitly add X-Requested-With header
518
+ .withoutCsrfProtection(); // Or disable all automatic CSRF protection
443
519
  ```
444
520
 
521
+ ### Integrating with Backend Frameworks
522
+
523
+ Most modern frameworks support CSRF protection out of the box. The library works seamlessly with:
524
+
525
+ - **Laravel**: Automatically reads XSRF-TOKEN cookie and sends X-XSRF-TOKEN header
526
+ - **Rails**: Works with the Rails CSRF token system
527
+ - **Django**: Compatible with Django's CSRF middleware
528
+ - **Express.js + csurf**: Works with the csurf middleware token pattern
529
+
530
+ When your server sends a CSRF token in a cookie or response header, `create-request` can automatically extract and include it in subsequent requests.
531
+
445
532
  ## Browser Support
446
533
 
447
534
  This library works with all browsers that support the Fetch API:
@@ -13,6 +13,7 @@ export declare abstract class BaseRequest {
13
13
  protected cacheEnabled: boolean;
14
14
  constructor();
15
15
  withHeaders(headers: Record<string, string>): this;
16
+ withHeader(key: string, value: string): this;
16
17
  withTimeout(timeout: number): this;
17
18
  withRetries(retries: number): this;
18
19
  onRetry(callback: RetryCallback): this;
@@ -34,6 +35,12 @@ export declare abstract class BaseRequest {
34
35
  * @returns The instance for chaining
35
36
  */
36
37
  withReferrer(referrer: string): this;
38
+ /**
39
+ * Sets the referrer policy for the request
40
+ * @param policy - The referrer policy to use
41
+ * @returns The instance for chaining
42
+ */
43
+ withReferrerPolicy(policy: ReferrerPolicy): this;
37
44
  /**
38
45
  * Sets the redirect policy for the request
39
46
  * @param redirect - Can be 'follow', 'error', or 'manual'
@@ -29,4 +29,5 @@ export interface ResponsePromise<T = ResponseWrapper> extends Promise<T> {
29
29
  getBlob(): Promise<Blob>;
30
30
  getArrayBuffer(): Promise<ArrayBuffer>;
31
31
  getBody(): Promise<ReadableStream<Uint8Array> | null>;
32
+ getData<T = unknown, R = T>(selector?: (data: T) => R): Promise<T | R>;
32
33
  }
@@ -70,3 +70,16 @@ export declare enum BodyType {
70
70
  STRING = "string",
71
71
  BINARY = "binary"
72
72
  }
73
+ /**
74
+ * Referrer policies for fetch requests
75
+ */
76
+ export declare enum ReferrerPolicy {
77
+ ORIGIN = "origin",
78
+ UNSAFE_URL = "unsafe-url",
79
+ SAME_ORIGIN = "same-origin",
80
+ NO_REFERRER = "no-referrer",
81
+ STRICT_ORIGIN = "strict-origin",
82
+ ORIGIN_WHEN_CROSS_ORIGIN = "origin-when-cross-origin",
83
+ NO_REFERRER_WHEN_DOWNGRADE = "no-referrer-when-downgrade",
84
+ STRICT_ORIGIN_WHEN_CROSS_ORIGIN = "strict-origin-when-cross-origin"
85
+ }
@@ -1,6 +1,6 @@
1
1
  import { get, put, head, post, patch, del, options } from "./requestFactories";
2
2
  import { Config } from "./utils/Config";
3
- export { HttpMethod, RequestPriority, CredentialsPolicy, RequestMode, RedirectMode, CacheMode, SameSitePolicy } from "./enums";
3
+ export { HttpMethod, RequestPriority, CredentialsPolicy, RequestMode, RedirectMode, CacheMode, SameSitePolicy, ReferrerPolicy } from "./enums";
4
4
  export type { RetryCallback, RequestOptions, CookieOptions, CookiesRecord } from "./types";
5
5
  export { RequestError } from "./RequestError";
6
6
  export { ResponseWrapper } from "./ResponseWrapper";
@@ -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 = {}) {
@@ -780,6 +794,9 @@ class BaseRequest {
780
794
  };
781
795
  return this;
782
796
  }
797
+ withHeader(key, value) {
798
+ return this.withHeaders({ [key]: value });
799
+ }
783
800
  withTimeout(timeout) {
784
801
  if (!Number.isFinite(timeout) || timeout <= 0)
785
802
  throw new Error("Timeout must be a positive number");
@@ -823,6 +840,15 @@ class BaseRequest {
823
840
  this.requestOptions.referrer = referrer;
824
841
  return this;
825
842
  }
843
+ /**
844
+ * Sets the referrer policy for the request
845
+ * @param policy - The referrer policy to use
846
+ * @returns The instance for chaining
847
+ */
848
+ withReferrerPolicy(policy) {
849
+ this.requestOptions.referrerPolicy = policy;
850
+ return this;
851
+ }
826
852
  /**
827
853
  * Sets the redirect policy for the request
828
854
  * @param redirect - Can be 'follow', 'error', or 'manual'
@@ -929,6 +955,12 @@ class BaseRequest {
929
955
  * Works in both browser and Node.js environments
930
956
  */
931
957
  encodeBase64(str) {
958
+ // Modern approach using TextEncoder (available in both modern browsers and Node.js)
959
+ if (typeof TextEncoder !== "undefined" && typeof btoa === "function") {
960
+ const encoder = new TextEncoder();
961
+ const bytes = encoder.encode(str);
962
+ return btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)]));
963
+ }
932
964
  // Browser environment
933
965
  if (typeof btoa === "function")
934
966
  return btoa(str);
@@ -1119,6 +1151,25 @@ class BaseRequest {
1119
1151
  responsePromise.getBody = async () => {
1120
1152
  return basePromise.then(response => response.getBody());
1121
1153
  };
1154
+ responsePromise.getData = async (selector) => {
1155
+ try {
1156
+ const data = await basePromise.then(response => response.getJson());
1157
+ // If no selector is provided, return the raw JSON data
1158
+ if (!selector)
1159
+ return data;
1160
+ // Apply the selector if provided
1161
+ return selector(data);
1162
+ }
1163
+ catch (error) {
1164
+ if (error instanceof Error) {
1165
+ // If selector fails, enhance the error with the original data
1166
+ const enhancedError = new Error(`Data selector failed: ${error.message}. Original data: ${JSON.stringify(await basePromise.then(response => response.getJson()), null, 2)}`);
1167
+ enhancedError.stack = error.stack;
1168
+ throw enhancedError;
1169
+ }
1170
+ throw error;
1171
+ }
1172
+ };
1122
1173
  return responsePromise;
1123
1174
  }
1124
1175
  /**
@@ -1447,4 +1498,4 @@ const create = {
1447
1498
  config: Config.getInstance(),
1448
1499
  };
1449
1500
 
1450
- 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 };
1501
+ export { CacheManager, CacheMode, CookieUtils, CredentialsPolicy, DeleteRequest, GetRequest, HeadRequest, HttpMethod, OptionsRequest, PatchRequest, PostRequest, PutRequest, RedirectMode, ReferrerPolicy, RequestError, RequestMode, RequestPriority, ResponseWrapper, SameSitePolicy, createLocalStorageStorage, createMemoryStorage, createSessionStorageStorage, create as default };
@@ -84,6 +84,20 @@
84
84
  BodyType["STRING"] = "string";
85
85
  BodyType["BINARY"] = "binary";
86
86
  })(BodyType || (BodyType = {}));
87
+ /**
88
+ * Referrer policies for fetch requests
89
+ */
90
+ exports.ReferrerPolicy = void 0;
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
+ })(exports.ReferrerPolicy || (exports.ReferrerPolicy = {}));
87
101
 
88
102
  class RequestError extends Error {
89
103
  constructor(message, url, method, options = {}) {
@@ -786,6 +800,9 @@
786
800
  };
787
801
  return this;
788
802
  }
803
+ withHeader(key, value) {
804
+ return this.withHeaders({ [key]: value });
805
+ }
789
806
  withTimeout(timeout) {
790
807
  if (!Number.isFinite(timeout) || timeout <= 0)
791
808
  throw new Error("Timeout must be a positive number");
@@ -829,6 +846,15 @@
829
846
  this.requestOptions.referrer = referrer;
830
847
  return this;
831
848
  }
849
+ /**
850
+ * Sets the referrer policy for the request
851
+ * @param policy - The referrer policy to use
852
+ * @returns The instance for chaining
853
+ */
854
+ withReferrerPolicy(policy) {
855
+ this.requestOptions.referrerPolicy = policy;
856
+ return this;
857
+ }
832
858
  /**
833
859
  * Sets the redirect policy for the request
834
860
  * @param redirect - Can be 'follow', 'error', or 'manual'
@@ -935,6 +961,12 @@
935
961
  * Works in both browser and Node.js environments
936
962
  */
937
963
  encodeBase64(str) {
964
+ // Modern approach using TextEncoder (available in both modern browsers and Node.js)
965
+ if (typeof TextEncoder !== "undefined" && typeof btoa === "function") {
966
+ const encoder = new TextEncoder();
967
+ const bytes = encoder.encode(str);
968
+ return btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)]));
969
+ }
938
970
  // Browser environment
939
971
  if (typeof btoa === "function")
940
972
  return btoa(str);
@@ -1125,6 +1157,25 @@
1125
1157
  responsePromise.getBody = async () => {
1126
1158
  return basePromise.then(response => response.getBody());
1127
1159
  };
1160
+ responsePromise.getData = async (selector) => {
1161
+ try {
1162
+ const data = await basePromise.then(response => response.getJson());
1163
+ // If no selector is provided, return the raw JSON data
1164
+ if (!selector)
1165
+ return data;
1166
+ // Apply the selector if provided
1167
+ return selector(data);
1168
+ }
1169
+ catch (error) {
1170
+ if (error instanceof Error) {
1171
+ // If selector fails, enhance the error with the original data
1172
+ const enhancedError = new Error(`Data selector failed: ${error.message}. Original data: ${JSON.stringify(await basePromise.then(response => response.getJson()), null, 2)}`);
1173
+ enhancedError.stack = error.stack;
1174
+ throw enhancedError;
1175
+ }
1176
+ throw error;
1177
+ }
1178
+ };
1128
1179
  return responsePromise;
1129
1180
  }
1130
1181
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-request",
3
- "version": "0.0.1-beta.7",
3
+ "version": "0.0.1-beta.9",
4
4
  "description": "A modern, chainable wrapper for fetch with automatic retries, timeouts, comprehensive error handling, and first-class TypeScript support",
5
5
  "main": "dist/library/index.js",
6
6
  "module": "dist/library/index.esm.js",