create-request 0.0.1-beta.7 → 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.
- package/README.md +218 -107
- package/dist/library/BaseRequest.d.ts +6 -0
- package/dist/library/ResponseWrapper.d.ts +1 -0
- package/dist/library/enums.d.ts +13 -0
- package/dist/library/index.esm.js +48 -0
- package/dist/library/index.js +48 -0
- package/package.json +1 -1
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
|
-
-
|
|
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
|
-
**
|
|
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
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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,78 @@ 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, CachePolicy, RedirectMode, ReferrerPolicy } from 'create-request';
|
|
129
100
|
|
|
130
101
|
// Configure request options
|
|
131
102
|
const request = create.get()
|
|
132
|
-
//
|
|
133
|
-
.withHeaders({ 'X-API-Key': 'abc123' })
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
.
|
|
138
|
-
|
|
139
|
-
|
|
103
|
+
// Basic headers
|
|
104
|
+
.withHeaders({ 'X-API-Key': 'abc123', 'Accept-Language': 'en-US' })
|
|
105
|
+
.withHeader('Custom-Header', 'value') // Add a single header
|
|
106
|
+
|
|
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
|
|
112
|
+
.withRetryDelay(1000) // Wait 1 second between retries
|
|
113
|
+
.withExponentialBackoff(true) // Use exponential backoff (increases wait time between retries)
|
|
114
|
+
.onRetry(({ attempt, error, delay }) => {
|
|
115
|
+
console.log(`Attempt ${attempt} failed: ${error.message}. Retrying in ${delay}ms`);
|
|
140
116
|
})
|
|
141
117
|
|
|
142
|
-
// Authentication
|
|
143
|
-
.withBearerToken('your-token')
|
|
118
|
+
// Authentication methods
|
|
119
|
+
.withBearerToken('your-token') // Adds Authorization: Bearer your-token
|
|
120
|
+
.withBasicAuth('username', 'password') // HTTP Basic Authentication
|
|
121
|
+
|
|
122
|
+
// URL parameters
|
|
123
|
+
.withQueryParams({ search: 'term', page: 1, limit: 20 })
|
|
124
|
+
.withQueryParam('filter', 'active') // Add a single query parameter
|
|
125
|
+
|
|
126
|
+
// Request body configuration (for POST/PUT/PATCH)
|
|
127
|
+
.withContentType('application/json') // Set specific content type
|
|
128
|
+
|
|
129
|
+
// Fetch API options
|
|
130
|
+
.withCredentials(CredentialsPolicy.INCLUDE) // Includes cookies with cross-origin requests
|
|
131
|
+
.withMode(RequestMode.CORS) // Controls CORS behavior
|
|
132
|
+
.withCache(CachePolicy.NO_STORE) // Controls how request interacts with browser cache
|
|
133
|
+
.withRedirect(RedirectMode.FOLLOW) // Controls redirect behavior (follow, error, manual)
|
|
134
|
+
.withReferrer('https://example.com') // Sets request referrer
|
|
135
|
+
.withReferrerPolicy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE) // Controls referrer policy
|
|
136
|
+
.withPriority(RequestPriority.HIGH) // Sets request priority
|
|
137
|
+
```
|
|
144
138
|
|
|
145
|
-
|
|
146
|
-
.withQueryParams({ search: 'term', page: 1 })
|
|
139
|
+
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:
|
|
147
140
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
141
|
+
```typescript
|
|
142
|
+
// Simple example with just what's needed
|
|
143
|
+
const searchUsers = create.get()
|
|
144
|
+
.withBearerToken(userToken)
|
|
145
|
+
.withQueryParams({ q: searchTerm, limit: 20 })
|
|
146
|
+
.withTimeout(3000);
|
|
147
|
+
|
|
148
|
+
// Now execute the request
|
|
149
|
+
const users = await searchUsers.sendTo('https://api.example.com/users').getData();
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
You can also create reusable base requests with common settings:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// Create base authenticated request
|
|
156
|
+
const apiBase = create.get()
|
|
157
|
+
.withHeaders({
|
|
158
|
+
'X-API-Version': '1.2',
|
|
159
|
+
'Accept-Language': 'en-US'
|
|
160
|
+
})
|
|
161
|
+
.withBearerToken(authToken)
|
|
162
|
+
.withTimeout(5000)
|
|
163
|
+
.withRetries(2);
|
|
164
|
+
|
|
165
|
+
// Use the base request for different endpoints
|
|
166
|
+
const users = await apiBase.sendTo('https://api.example.com/users').getData();
|
|
167
|
+
const products = await apiBase.sendTo('https://api.example.com/products').getData();
|
|
151
168
|
```
|
|
152
169
|
|
|
153
170
|
### Request Bodies (POST/PUT/PATCH)
|
|
@@ -182,6 +199,58 @@ const textData = await request.sendTo('https://api.example.com/endpoint').getTex
|
|
|
182
199
|
const blobData = await request.sendTo('https://api.example.com/endpoint').getBlob();
|
|
183
200
|
const bodyStream = await request.sendTo('https://api.example.com/endpoint').getBody();
|
|
184
201
|
const arrayBuffer = await request.sendTo('https://api.example.com/endpoint').getArrayBuffer();
|
|
202
|
+
|
|
203
|
+
// Using the data selector API to extract specific data
|
|
204
|
+
const userData = await request.sendTo('https://api.example.com/users')
|
|
205
|
+
.getData(data => data.results.users);
|
|
206
|
+
|
|
207
|
+
// Using the data selector without a selector function just returns the full JSON response
|
|
208
|
+
const fullData = await request.sendTo('https://api.example.com/data').getData();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Data Selection
|
|
212
|
+
|
|
213
|
+
The `getData` method provides a powerful way to extract and transform specific data from API responses:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
// Extract specific properties from nested structures
|
|
217
|
+
const posts = await request
|
|
218
|
+
.sendTo('https://api.example.com/feed')
|
|
219
|
+
.getData(data => data.feed.posts);
|
|
220
|
+
|
|
221
|
+
// Transform data in the selector function
|
|
222
|
+
const usernames = await request
|
|
223
|
+
.sendTo('https://api.example.com/users')
|
|
224
|
+
.getData(data => data.users.map(user => user.username));
|
|
225
|
+
|
|
226
|
+
// Apply filtering in the selector
|
|
227
|
+
const activeUsers = await request
|
|
228
|
+
.sendTo('https://api.example.com/users')
|
|
229
|
+
.getData(data => data.users.filter(user => user.isActive));
|
|
230
|
+
|
|
231
|
+
// Combine data from complex nested structures
|
|
232
|
+
const combinedData = await request
|
|
233
|
+
.sendTo('https://api.example.com/dashboard')
|
|
234
|
+
.getData(data => ({
|
|
235
|
+
userCount: data.stats.users.total,
|
|
236
|
+
recentPosts: data.content.recent.slice(0, 5),
|
|
237
|
+
notifications: data.user.notifications.unread
|
|
238
|
+
}));
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
When a selector fails, the error message will contain the original response data to help diagnose the issue:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
try {
|
|
245
|
+
const result = await request
|
|
246
|
+
.sendTo('https://api.example.com/data')
|
|
247
|
+
.getData(data => data.results.items); // Will fail if structure is different
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error(error);
|
|
250
|
+
// Error message includes the original response data:
|
|
251
|
+
// "Data selector failed: Cannot read properties of undefined (reading 'items').
|
|
252
|
+
// Original data: { "status": "error", "message": "Invalid request" }"
|
|
253
|
+
}
|
|
185
254
|
```
|
|
186
255
|
|
|
187
256
|
### Error Handling
|
|
@@ -285,12 +354,33 @@ interface User {
|
|
|
285
354
|
email: string;
|
|
286
355
|
}
|
|
287
356
|
|
|
357
|
+
interface ApiResponse {
|
|
358
|
+
users: User[];
|
|
359
|
+
pagination: {
|
|
360
|
+
total: number;
|
|
361
|
+
page: number;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
288
365
|
const userRequest = create.get();
|
|
289
|
-
const user = await userRequest.sendTo('https://api.example.com/user/123')
|
|
290
|
-
.getJson<User>();
|
|
291
366
|
|
|
292
|
-
//
|
|
293
|
-
|
|
367
|
+
// Type the full response
|
|
368
|
+
const response = await userRequest
|
|
369
|
+
.sendTo('https://api.example.com/users')
|
|
370
|
+
.getJson<ApiResponse>();
|
|
371
|
+
|
|
372
|
+
// Or use getData with type parameters
|
|
373
|
+
const users = await userRequest
|
|
374
|
+
.sendTo('https://api.example.com/users')
|
|
375
|
+
.getData<ApiResponse, User[]>(data => data.users);
|
|
376
|
+
|
|
377
|
+
// Or just get the full typed response
|
|
378
|
+
const fullData = await userRequest
|
|
379
|
+
.sendTo('https://api.example.com/users')
|
|
380
|
+
.getData<ApiResponse>();
|
|
381
|
+
|
|
382
|
+
// TypeScript knows the types
|
|
383
|
+
console.log(users[0].name);
|
|
294
384
|
```
|
|
295
385
|
|
|
296
386
|
### Promise Chaining
|
|
@@ -322,15 +412,6 @@ const request = create.get()
|
|
|
322
412
|
storage: createMemoryStorage(),
|
|
323
413
|
keyGenerator: (url, method, headers) => {
|
|
324
414
|
// Include user ID from authorization header in the cache key
|
|
325
|
-
const authHeader = headers?.['Authorization'] || '';
|
|
326
|
-
const userId = extractUserIdFromToken(authHeader);
|
|
327
|
-
return `${method}:${url}:${userId}`;
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
// Simple custom storage provider example
|
|
332
|
-
const customStorage: StorageProvider = {
|
|
333
|
-
get(key: string): string | null {
|
|
334
415
|
// Get from your custom storage
|
|
335
416
|
return localStorage.getItem(`my-app:${key}`);
|
|
336
417
|
},
|
|
@@ -424,24 +505,54 @@ With custom storage providers, you can integrate with any storage system, such a
|
|
|
424
505
|
|
|
425
506
|
## CSRF Protection
|
|
426
507
|
|
|
508
|
+
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.
|
|
509
|
+
|
|
510
|
+
### How CSRF Protection Works
|
|
511
|
+
|
|
512
|
+
The library employs multiple strategies to protect against CSRF attacks:
|
|
513
|
+
|
|
514
|
+
1. **Automatic X-Requested-With Header**: By default, all requests include the `X-Requested-With: XMLHttpRequest` header, which helps servers identify legitimate AJAX requests.
|
|
515
|
+
|
|
516
|
+
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.
|
|
517
|
+
|
|
518
|
+
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.
|
|
519
|
+
|
|
427
520
|
### Global CSRF Configuration
|
|
428
521
|
|
|
522
|
+
You can configure CSRF settings globally for all requests:
|
|
523
|
+
|
|
429
524
|
```typescript
|
|
430
525
|
// Configure CSRF settings for all requests
|
|
431
526
|
create.config.setCsrfToken('your-csrf-token');
|
|
432
|
-
create.config.setCsrfHeaderName('X-CSRF-Token');
|
|
433
|
-
create.config.setXsrfCookieName('XSRF-TOKEN');
|
|
527
|
+
create.config.setCsrfHeaderName('X-CSRF-Token'); // Default header name
|
|
528
|
+
create.config.setXsrfCookieName('XSRF-TOKEN'); // Default cookie name
|
|
529
|
+
create.config.enableAntiCsrf(true); // Enable/disable X-Requested-With header
|
|
530
|
+
create.config.enableAutoXsrf(true); // Enable/disable automatic cookie-to-header token
|
|
434
531
|
```
|
|
435
532
|
|
|
436
533
|
### Per-Request CSRF Settings
|
|
437
534
|
|
|
535
|
+
You can also configure CSRF protection on individual requests:
|
|
536
|
+
|
|
438
537
|
```typescript
|
|
439
538
|
// Configure CSRF for a specific request
|
|
440
539
|
const request = create.post()
|
|
441
|
-
.withCsrfToken('request-specific-token')
|
|
442
|
-
.
|
|
540
|
+
.withCsrfToken('request-specific-token') // Set a specific token
|
|
541
|
+
.withAntiCsrfHeaders() // Explicitly add X-Requested-With header
|
|
542
|
+
.withoutCsrfProtection(); // Or disable all automatic CSRF protection
|
|
443
543
|
```
|
|
444
544
|
|
|
545
|
+
### Integrating with Backend Frameworks
|
|
546
|
+
|
|
547
|
+
Most modern frameworks support CSRF protection out of the box. The library works seamlessly with:
|
|
548
|
+
|
|
549
|
+
- **Laravel**: Automatically reads XSRF-TOKEN cookie and sends X-XSRF-TOKEN header
|
|
550
|
+
- **Rails**: Works with the Rails CSRF token system
|
|
551
|
+
- **Django**: Compatible with Django's CSRF middleware
|
|
552
|
+
- **Express.js + csurf**: Works with the csurf middleware token pattern
|
|
553
|
+
|
|
554
|
+
When your server sends a CSRF token in a cookie or response header, `create-request` can automatically extract and include it in subsequent requests.
|
|
555
|
+
|
|
445
556
|
## Browser Support
|
|
446
557
|
|
|
447
558
|
This library works with all browsers that support the Fetch API:
|
|
@@ -34,6 +34,12 @@ export declare abstract class BaseRequest {
|
|
|
34
34
|
* @returns The instance for chaining
|
|
35
35
|
*/
|
|
36
36
|
withReferrer(referrer: string): this;
|
|
37
|
+
/**
|
|
38
|
+
* Sets the referrer policy for the request
|
|
39
|
+
* @param policy - The referrer policy to use
|
|
40
|
+
* @returns The instance for chaining
|
|
41
|
+
*/
|
|
42
|
+
withReferrerPolicy(policy: ReferrerPolicy): this;
|
|
37
43
|
/**
|
|
38
44
|
* Sets the redirect policy for the request
|
|
39
45
|
* @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
|
}
|
package/dist/library/enums.d.ts
CHANGED
|
@@ -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
|
+
}
|
|
@@ -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 = {}) {
|
|
@@ -823,6 +837,15 @@ class BaseRequest {
|
|
|
823
837
|
this.requestOptions.referrer = referrer;
|
|
824
838
|
return this;
|
|
825
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
|
+
}
|
|
826
849
|
/**
|
|
827
850
|
* Sets the redirect policy for the request
|
|
828
851
|
* @param redirect - Can be 'follow', 'error', or 'manual'
|
|
@@ -929,6 +952,12 @@ class BaseRequest {
|
|
|
929
952
|
* Works in both browser and Node.js environments
|
|
930
953
|
*/
|
|
931
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
|
+
}
|
|
932
961
|
// Browser environment
|
|
933
962
|
if (typeof btoa === "function")
|
|
934
963
|
return btoa(str);
|
|
@@ -1119,6 +1148,25 @@ class BaseRequest {
|
|
|
1119
1148
|
responsePromise.getBody = async () => {
|
|
1120
1149
|
return basePromise.then(response => response.getBody());
|
|
1121
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
|
+
};
|
|
1122
1170
|
return responsePromise;
|
|
1123
1171
|
}
|
|
1124
1172
|
/**
|
package/dist/library/index.js
CHANGED
|
@@ -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
|
+
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 = {}));
|
|
87
101
|
|
|
88
102
|
class RequestError extends Error {
|
|
89
103
|
constructor(message, url, method, options = {}) {
|
|
@@ -829,6 +843,15 @@
|
|
|
829
843
|
this.requestOptions.referrer = referrer;
|
|
830
844
|
return this;
|
|
831
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
|
+
}
|
|
832
855
|
/**
|
|
833
856
|
* Sets the redirect policy for the request
|
|
834
857
|
* @param redirect - Can be 'follow', 'error', or 'manual'
|
|
@@ -935,6 +958,12 @@
|
|
|
935
958
|
* Works in both browser and Node.js environments
|
|
936
959
|
*/
|
|
937
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
|
+
}
|
|
938
967
|
// Browser environment
|
|
939
968
|
if (typeof btoa === "function")
|
|
940
969
|
return btoa(str);
|
|
@@ -1125,6 +1154,25 @@
|
|
|
1125
1154
|
responsePromise.getBody = async () => {
|
|
1126
1155
|
return basePromise.then(response => response.getBody());
|
|
1127
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
|
+
};
|
|
1128
1176
|
return responsePromise;
|
|
1129
1177
|
}
|
|
1130
1178
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-request",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.8",
|
|
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",
|