create-request 1.4.3-rc.0 → 1.4.3-rc.2

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
@@ -16,15 +16,16 @@
16
16
  - [Why create-request](#why-create-request)
17
17
  - [Installation](#installation)
18
18
  - [Basic Usage](#basic-usage)
19
- - [API Builder](#api-builder)
20
- - [Automatic Retries with Delay](#automatic-retries-with-delay)
21
- - [Interceptors](#interceptors)
22
- - [Request Cancellation](#request-cancellation)
23
19
  - [URL Handling](#url-handling)
24
- - [Data Selection](#data-selection)
25
- - [GraphQL Support](#graphql-requests)
26
- - [TypeScript Support](#typescript-support)
27
- - [CSRF Protection](#csrf-protection)
20
+ - [Advanced Usage](#advanced-usage)
21
+ - [API Builder](#api-builder)
22
+ - [Automatic Retries with Delay](#automatic-retries-with-delay)
23
+ - [Interceptors](#interceptors)
24
+ - [Request Cancellation](#request-cancellation)
25
+ - [Data Selection](#data-selection)
26
+ - [TypeScript Support](#typescript-support)
27
+ - [CSRF Protection](#csrf-protection)
28
+ - [Subresource Integrity and Cache Control](#subresource-integrity-and-cache-control)
28
29
  - [Performance Considerations](#performance-considerations)
29
30
  - [Browser Support](#browser-support)
30
31
  - [Comparison of JavaScript HTTP Client Libraries](#comparison-of-javascript-http-client-libraries)
@@ -310,44 +311,6 @@ const merged = create
310
311
  .withQueryParams({ new: "param" }); // Both existing and new params included
311
312
  ```
312
313
 
313
- ### Subresource Integrity and Cache Control
314
-
315
- The library supports subresource integrity verification and cache control options:
316
-
317
- ```typescript
318
- // Subresource Integrity - ensures the fetched resource hasn't been tampered with
319
- const secureRequest = create
320
- .get("https://cdn.example.com/script.js")
321
- .withIntegrity("sha256-abcdef1234567890..."); // Browser will verify the hash
322
-
323
- // Cache Control - supports all cache modes via fluent API or string values
324
- const cachedRequest = create.get("https://api.example.com/data").withCache("no-cache"); // Direct string value
325
-
326
- // Using fluent API for cache modes
327
- const fluentCache = create.get("https://api.example.com/data").withCache.NO_CACHE(); // Fluent API method
328
-
329
- // All available cache modes:
330
- create
331
- .get("https://api.example.com/data")
332
- .withCache.DEFAULT() // Default cache behavior
333
- .withCache.NO_STORE() // Don't store in cache
334
- .withCache.RELOAD() // Reload from server
335
- .withCache.NO_CACHE() // Validate with server before using cache
336
- .withCache.FORCE_CACHE() // Use cache even if stale
337
- .withCache.ONLY_IF_CACHED(); // Only use cache, don't fetch from server
338
-
339
- // Using enum values (import from create-request)
340
- import { CacheMode } from "create-request";
341
-
342
- const enumCache = create.get("https://api.example.com/data").withCache(CacheMode.NO_CACHE);
343
-
344
- // Combining integrity and cache
345
- const secureCached = create
346
- .get("https://cdn.example.com/resource.js")
347
- .withIntegrity("sha256-abcdef1234567890...")
348
- .withCache("no-store"); // Ensure no caching for sensitive resources
349
- ```
350
-
351
314
  ### Executing Requests
352
315
 
353
316
  ```typescript
@@ -418,7 +381,34 @@ try {
418
381
  }
419
382
  ```
420
383
 
421
- ## API Builder
384
+ ## URL Handling
385
+
386
+ The library handles both absolute and relative URLs, and automatically merges query parameters:
387
+
388
+ ```typescript
389
+ // Relative URLs (preserved as-is)
390
+ const relative = await create.get("/api/users").getJson();
391
+
392
+ // Absolute URLs
393
+ const absolute = await create.get("https://api.example.com/users").getJson();
394
+
395
+ // Merging query params with existing URL params
396
+ const merged = await create
397
+ .get("https://api.example.com/users?page=1")
398
+ .withQueryParams({ limit: 20, sort: "name" })
399
+ .getJson();
400
+ // Result: https://api.example.com/users?page=1&limit=20&sort=name
401
+
402
+ // Special characters and unicode are properly encoded
403
+ const encoded = await create
404
+ .get("https://api.example.com/search")
405
+ .withQueryParams({ name: "用户名", filter: "status:active" })
406
+ .getJson();
407
+ ```
408
+
409
+ ## Advanced Usage
410
+
411
+ ### API Builder
422
412
 
423
413
  The API builder allows you to create configured API instances with default settings that can be reused across your application. This is perfect for setting up a base URL, default headers, timeout values, and other request configurations once and using them for all requests.
424
414
 
@@ -467,8 +457,6 @@ The API builder provides access to all request configuration methods from `BaseR
467
457
  - `withReferrer(referrer)` - Set default referrer
468
458
  - `withKeepAlive(keepalive)` - Configure keep-alive
469
459
  - `withIntegrity(integrity)` - Set integrity check
470
- - `withQueryParams(params)` - Add default query parameters
471
- - `withQueryParam(key, value)` - Add a single default query parameter
472
460
 
473
461
  **CSRF Protection:**
474
462
 
@@ -498,26 +486,6 @@ await api.get("/users").getJson();
498
486
  await api.post("/posts").withBody({ title: "Hello" }).getJson();
499
487
  ```
500
488
 
501
- #### Methods NOT Available on API Builder
502
-
503
- The following methods are **not available** on the API builder because they are request-specific and don't make sense as defaults:
504
-
505
- - **`withAbortController(controller)`** - AbortController is per-request, not a default
506
- - **`withBody(body)`** - Request bodies are different for each request
507
- - **`withGraphQL(query, variables, options)`** - GraphQL queries are request-specific
508
-
509
- These methods should be called directly on individual request instances:
510
-
511
- ```typescript
512
- const api = create.api().withBaseURL("https://api.example.com");
513
-
514
- // ✅ Good: Use withBody on individual requests
515
- await api.post("/users").withBody({ name: "John" }).getJson();
516
-
517
- // ❌ Bad: withBody is not available on the API builder
518
- // api.withBody({ name: "John" }); // This will be undefined
519
- ```
520
-
521
489
  #### URL Resolution
522
490
 
523
491
  The API builder intelligently resolves URLs:
@@ -622,7 +590,7 @@ async function deleteUser(id: string) {
622
590
  }
623
591
  ```
624
592
 
625
- ## Automatic Retries with Delay
593
+ ### Automatic Retries with Delay
626
594
 
627
595
  The `withRetries()` method supports both simple number-based retries and object-based configuration with customizable delays:
628
596
 
@@ -669,7 +637,7 @@ const request4 = create.get("https://api.example.com/data").withRetries({
669
637
  })
670
638
  ```
671
639
 
672
- ## Interceptors
640
+ ### Interceptors
673
641
 
674
642
  Interceptors allow you to modify requests, transform responses, or handle errors globally or per-request. This is perfect for adding authentication tokens, logging, error recovery, and more.
675
643
 
@@ -803,7 +771,7 @@ const asyncData = await create
803
771
  .getJson();
804
772
  ```
805
773
 
806
- ## Request Cancellation
774
+ ### Request Cancellation
807
775
 
808
776
  ```typescript
809
777
  const controller = new AbortController();
@@ -829,32 +797,7 @@ try {
829
797
  }
830
798
  ```
831
799
 
832
- ## URL Handling
833
-
834
- The library handles both absolute and relative URLs, and automatically merges query parameters:
835
-
836
- ```typescript
837
- // Relative URLs (preserved as-is)
838
- const relative = await create.get("/api/users").getJson();
839
-
840
- // Absolute URLs
841
- const absolute = await create.get("https://api.example.com/users").getJson();
842
-
843
- // Merging query params with existing URL params
844
- const merged = await create
845
- .get("https://api.example.com/users?page=1")
846
- .withQueryParams({ limit: 20, sort: "name" })
847
- .getJson();
848
- // Result: https://api.example.com/users?page=1&limit=20&sort=name
849
-
850
- // Special characters and unicode are properly encoded
851
- const encoded = await create
852
- .get("https://api.example.com/search")
853
- .withQueryParams({ name: "用户名", filter: "status:active" })
854
- .getJson();
855
- ```
856
-
857
- ## Data Selection
800
+ ### Data Selection
858
801
 
859
802
  The `getData` method provides a powerful way to extract and transform specific data from API responses:
860
803
 
@@ -896,7 +839,7 @@ try {
896
839
  }
897
840
  ```
898
841
 
899
- ## TypeScript Support
842
+ ### TypeScript Support
900
843
 
901
844
  ```typescript
902
845
  interface User {
@@ -942,7 +885,7 @@ async function getUserById(id: number): Promise<User> {
942
885
  }
943
886
  ```
944
887
 
945
- ## CSRF Protection
888
+ ### CSRF Protection
946
889
 
947
890
  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.
948
891
 
@@ -986,6 +929,44 @@ const request = create
986
929
  .withoutCsrfProtection(); // Or disable all automatic CSRF protection
987
930
  ```
988
931
 
932
+ ### Subresource Integrity and Cache Control
933
+
934
+ The library supports subresource integrity verification and cache control options:
935
+
936
+ ```typescript
937
+ // Subresource Integrity - ensures the fetched resource hasn't been tampered with
938
+ const secureRequest = create
939
+ .get("https://cdn.example.com/script.js")
940
+ .withIntegrity("sha256-abcdef1234567890..."); // Browser will verify the hash
941
+
942
+ // Cache Control - supports all cache modes via fluent API or string values
943
+ const cachedRequest = create.get("https://api.example.com/data").withCache("no-cache"); // Direct string value
944
+
945
+ // Using fluent API for cache modes
946
+ const fluentCache = create.get("https://api.example.com/data").withCache.NO_CACHE(); // Fluent API method
947
+
948
+ // All available cache modes:
949
+ create
950
+ .get("https://api.example.com/data")
951
+ .withCache.DEFAULT() // Default cache behavior
952
+ .withCache.NO_STORE() // Don't store in cache
953
+ .withCache.RELOAD() // Reload from server
954
+ .withCache.NO_CACHE() // Validate with server before using cache
955
+ .withCache.FORCE_CACHE() // Use cache even if stale
956
+ .withCache.ONLY_IF_CACHED(); // Only use cache, don't fetch from server
957
+
958
+ // Using enum values (import from create-request)
959
+ import { CacheMode } from "create-request";
960
+
961
+ const enumCache = create.get("https://api.example.com/data").withCache(CacheMode.NO_CACHE);
962
+
963
+ // Combining integrity and cache
964
+ const secureCached = create
965
+ .get("https://cdn.example.com/resource.js")
966
+ .withIntegrity("sha256-abcdef1234567890...")
967
+ .withCache("no-store"); // Ensure no caching for sensitive resources
968
+ ```
969
+
989
970
  ## Performance Considerations
990
971
 
991
972
  create-request is designed to be lightweight and efficient:
@@ -10,6 +10,7 @@ export declare class RequestError extends Error {
10
10
  response?: Response;
11
11
  isTimeout?: boolean;
12
12
  isAborted?: boolean;
13
+ cause?: Error;
13
14
  });
14
15
  /**
15
16
  * Static methods for creating specific types of RequestError
@@ -3,16 +3,25 @@ import type { GraphQLOptions } from "./types.js";
3
3
  * Wrapper for HTTP responses with methods to transform the response data
4
4
  */
5
5
  export declare class ResponseWrapper {
6
- private readonly response;
7
6
  readonly url?: string;
8
7
  readonly method?: string;
8
+ private readonly response;
9
9
  private graphQLOptions?;
10
+ private cachedBlob?;
11
+ private cachedText?;
12
+ private cachedJson?;
13
+ private cachedArrayBuffer?;
10
14
  constructor(response: Response, url?: string, method?: string, graphQLOptions?: GraphQLOptions);
11
15
  get status(): number;
12
16
  get statusText(): string;
13
17
  get headers(): Headers;
14
18
  get ok(): boolean;
15
19
  get raw(): Response;
20
+ /**
21
+ * Check if the response body has already been consumed and throw an error if so
22
+ * @throws RequestError if the body has already been consumed
23
+ */
24
+ private checkBodyNotConsumed;
16
25
  /**
17
26
  * Check for GraphQL errors and throw if throwOnError is enabled
18
27
  * @param data - The parsed JSON data
@@ -21,11 +30,10 @@ export declare class ResponseWrapper {
21
30
  private checkGraphQLErrors;
22
31
  /**
23
32
  * Parse the response body as JSON
24
- * Note: This consumes the response body and can only be called once.
25
33
  * If GraphQL options are set with throwOnError=true, will check for GraphQL errors and throw.
26
34
  *
27
35
  * @returns The parsed JSON data
28
- * @throws {RequestError} When the request fails, JSON parsing fails, GraphQL errors occur (if throwOnError enabled), or body is already consumed
36
+ * @throws {RequestError} When the request fails, JSON parsing fails, or GraphQL errors occur (if throwOnError enabled).
29
37
  *
30
38
  * @example
31
39
  * const data = await response.getJson();
@@ -44,10 +52,9 @@ export declare class ResponseWrapper {
44
52
  getJson<T = unknown>(): Promise<T>;
45
53
  /**
46
54
  * Get the response body as text
47
- * Note: This consumes the response body and can only be called once.
48
55
  *
49
56
  * @returns The response text
50
- * @throws {RequestError} When reading fails or the response has already been consumed
57
+ * @throws {RequestError} When reading fails
51
58
  *
52
59
  * @example
53
60
  * const text = await response.getText();
@@ -55,10 +62,9 @@ export declare class ResponseWrapper {
55
62
  getText(): Promise<string>;
56
63
  /**
57
64
  * Get the response body as a Blob
58
- * Note: This consumes the response body and can only be called once.
59
65
  *
60
66
  * @returns The response as a Blob
61
- * @throws {RequestError} When reading fails or the response has already been consumed
67
+ * @throws {RequestError} When reading fails
62
68
  *
63
69
  * @example
64
70
  * const blob = await response.getBlob();
@@ -67,10 +73,9 @@ export declare class ResponseWrapper {
67
73
  getBlob(): Promise<Blob>;
68
74
  /**
69
75
  * Get the response body as an ArrayBuffer
70
- * Note: This consumes the response body and can only be called once.
71
76
  *
72
77
  * @returns The response as an ArrayBuffer
73
- * @throws {RequestError} When reading fails or the response has already been consumed
78
+ * @throws {RequestError} When reading fails
74
79
  *
75
80
  * @example
76
81
  * const buffer = await response.getArrayBuffer();
@@ -80,6 +85,7 @@ export declare class ResponseWrapper {
80
85
  /**
81
86
  * Get the raw response body as a ReadableStream
82
87
  * Note: This consumes the response body and should only be called once.
88
+ * Unlike other methods, streams cannot be cached, so this will throw if the body is already consumed.
83
89
  *
84
90
  * @returns The response body as a ReadableStream or null
85
91
  * @throws {RequestError} When the response body has already been consumed
@@ -20,8 +20,6 @@ interface ApiBuilderRequestMethods {
20
20
  withCookie(name: string, value: string | CookieOptions): ApiBuilder;
21
21
  withRequestInterceptor(interceptor: RequestInterceptor): ApiBuilder;
22
22
  withResponseInterceptor(interceptor: ResponseInterceptor): ApiBuilder;
23
- withQueryParam(key: string, value: string | string[] | number | boolean | null | undefined): ApiBuilder;
24
- withQueryParams(params: Record<string, string | string[] | number | boolean | null | undefined>): ApiBuilder;
25
23
  }
26
24
  /**
27
25
  * API builder for creating configured API instances with default settings.