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.
@@ -1,27 +1,100 @@
1
- import { type HttpMethod, type RedirectMode, type RequestMode, type RequestPriority, CredentialsPolicy } from "./enums";
2
- import { type ResponsePromise } from "./ResponseWrapper";
3
- import type { RequestOptions, RetryCallback, CookiesRecord, CookieOptions } from "./types";
4
- import type { CacheOptions } from "./types/cache";
5
- import { CacheManager } from "./utils/CacheManager";
1
+ import { type HttpMethod, RedirectMode, RequestMode, RequestPriority, ReferrerPolicy, CredentialsPolicy } from "./enums.js";
2
+ import type { RequestOptions, RetryCallback, CookiesRecord, CookieOptions, RequestInterceptor, ResponseInterceptor, ErrorInterceptor } from "./types.js";
3
+ import { ResponseWrapper } from "./ResponseWrapper.js";
4
+ /**
5
+ * Base class with common functionality for all request types
6
+ * Provides the core request building and execution capabilities.
7
+ */
6
8
  export declare abstract class BaseRequest {
7
9
  protected abstract method: HttpMethod;
10
+ protected url: string;
8
11
  protected requestOptions: RequestOptions;
9
12
  protected abortController?: AbortController;
10
13
  protected queryParams: URLSearchParams;
11
14
  protected autoApplyCsrfProtection: boolean;
12
- protected cacheManager?: CacheManager;
13
- protected cacheEnabled: boolean;
14
- constructor();
15
+ private requestInterceptors;
16
+ private responseInterceptors;
17
+ private errorInterceptors;
18
+ constructor(url: string);
19
+ /**
20
+ * Creates a fluent API for setting enum-based options
21
+ * Combines direct setter with convenience methods
22
+ */
23
+ private createFluentSetter;
24
+ private validateUrl;
25
+ /**
26
+ * Add multiple HTTP headers to the request
27
+ *
28
+ * @param headers - Key-value pairs of header names and values
29
+ * @returns The request instance for chaining
30
+ *
31
+ * @example
32
+ * request.withHeaders({
33
+ * 'Accept': 'application/json',
34
+ * 'X-Custom-Header': 'value'
35
+ * });
36
+ */
15
37
  withHeaders(headers: Record<string, string>): this;
38
+ /**
39
+ * Add a single HTTP header to the request
40
+ *
41
+ * @param key - The header name
42
+ * @param value - The header value
43
+ * @returns The request instance for chaining
44
+ *
45
+ * @example
46
+ * request.withHeader('Accept', 'application/json');
47
+ */
48
+ withHeader(key: string, value: string): this;
49
+ /**
50
+ * Set a timeout for the request
51
+ * If the request takes longer than the specified timeout, it will be aborted.
52
+ *
53
+ * @param timeout - The timeout in milliseconds
54
+ * @returns The request instance for chaining
55
+ * @throws Error if timeout is not a positive number
56
+ *
57
+ * @example
58
+ * request.withTimeout(5000); // 5 seconds timeout
59
+ */
16
60
  withTimeout(timeout: number): this;
61
+ /**
62
+ * Configure automatic retry behavior for failed requests
63
+ *
64
+ * @param retries - Number of retry attempts before failing
65
+ * @returns The request instance for chaining
66
+ * @throws Error if retries is not a non-negative integer
67
+ *
68
+ * @example
69
+ * request.withRetries(3); // Retry up to 3 times
70
+ */
17
71
  withRetries(retries: number): this;
72
+ /**
73
+ * Set a callback to be invoked before each retry attempt
74
+ * Useful for implementing backoff strategies or logging retry attempts.
75
+ *
76
+ * @param callback - Function to call before retrying
77
+ * @returns The request instance for chaining
78
+ *
79
+ * @example
80
+ * request.onRetry(({ attempt, error }) => {
81
+ * console.log(`Retry attempt ${attempt} after error: ${error.message}`);
82
+ * return new Promise(resolve => setTimeout(resolve, attempt * 1000));
83
+ * });
84
+ */
18
85
  onRetry(callback: RetryCallback): this;
19
86
  /**
20
- * Sets the credentials policy for the request
21
- * @param credentialsPolicy - Can be 'omit', 'same-origin', or 'include'
22
- * @returns The instance for chaining
87
+ * Sets credentials policy - supports both direct call and fluent API
88
+ * @example
89
+ * request.withCredentials("include")
90
+ * request.withCredentials(CredentialsPolicy.INCLUDE)
91
+ * request.withCredentials.INCLUDE()
23
92
  */
24
- withCredentials(credentialsPolicy?: CredentialsPolicy): this;
93
+ get withCredentials(): ((credentialsPolicy: CredentialsPolicy | string) => BaseRequest) & {
94
+ INCLUDE: () => BaseRequest;
95
+ OMIT: () => BaseRequest;
96
+ SAME_ORIGIN: () => BaseRequest;
97
+ };
25
98
  /**
26
99
  * Allows providing an external AbortController to cancel the request
27
100
  * @param controller - The AbortController to use for this request
@@ -35,17 +108,34 @@ export declare abstract class BaseRequest {
35
108
  */
36
109
  withReferrer(referrer: string): this;
37
110
  /**
38
- * Sets the referrer policy for the request
39
- * @param policy - The referrer policy to use
40
- * @returns The instance for chaining
111
+ * Sets referrer policy - supports both direct call and fluent API
112
+ * @example
113
+ * request.withReferrerPolicy("no-referrer")
114
+ * request.withReferrerPolicy(ReferrerPolicy.NO_REFERRER)
115
+ * request.withReferrerPolicy.NO_REFERRER()
41
116
  */
42
- withReferrerPolicy(policy: ReferrerPolicy): this;
117
+ get withReferrerPolicy(): ((policy: ReferrerPolicy | string) => BaseRequest) & {
118
+ ORIGIN: () => BaseRequest;
119
+ UNSAFE_URL: () => BaseRequest;
120
+ SAME_ORIGIN: () => BaseRequest;
121
+ NO_REFERRER: () => BaseRequest;
122
+ STRICT_ORIGIN: () => BaseRequest;
123
+ ORIGIN_WHEN_CROSS_ORIGIN: () => BaseRequest;
124
+ NO_REFERRER_WHEN_DOWNGRADE: () => BaseRequest;
125
+ STRICT_ORIGIN_WHEN_CROSS_ORIGIN: () => BaseRequest;
126
+ };
43
127
  /**
44
- * Sets the redirect policy for the request
45
- * @param redirect - Can be 'follow', 'error', or 'manual'
46
- * @returns The instance for chaining
128
+ * Sets redirect mode - supports both direct call and fluent API
129
+ * @example
130
+ * request.withRedirect("follow")
131
+ * request.withRedirect(RedirectMode.FOLLOW)
132
+ * request.withRedirect.FOLLOW()
47
133
  */
48
- withRedirect(redirect: RedirectMode): this;
134
+ get withRedirect(): ((redirect: RedirectMode | string) => BaseRequest) & {
135
+ FOLLOW: () => BaseRequest;
136
+ ERROR: () => BaseRequest;
137
+ MANUAL: () => BaseRequest;
138
+ };
49
139
  /**
50
140
  * Sets the keepalive flag for the request
51
141
  * @param keepalive - Whether to allow the request to outlive the page
@@ -53,11 +143,17 @@ export declare abstract class BaseRequest {
53
143
  */
54
144
  withKeepAlive(keepalive: boolean): this;
55
145
  /**
56
- * Sets the priority of the request
57
- * @param priority - Can be 'high', 'low', or 'auto'
58
- * @returns The instance for chaining
146
+ * Sets request priority - supports both direct call and fluent API
147
+ * @example
148
+ * request.withPriority("high")
149
+ * request.withPriority(RequestPriority.HIGH)
150
+ * request.withPriority.HIGH()
59
151
  */
60
- withPriority(priority: RequestPriority): this;
152
+ get withPriority(): ((priority: RequestPriority | string) => BaseRequest) & {
153
+ HIGH: () => BaseRequest;
154
+ LOW: () => BaseRequest;
155
+ AUTO: () => BaseRequest;
156
+ };
61
157
  /**
62
158
  * Adds query parameters to the request URL
63
159
  * @param params - An object containing the query parameters
@@ -72,11 +168,18 @@ export declare abstract class BaseRequest {
72
168
  */
73
169
  withQueryParam(key: string, value: string | string[] | number | boolean | null | undefined): this;
74
170
  /**
75
- * Sets the mode of the request
76
- * @param mode - The mode for the request (cors, no-cors, same-origin, navigate)
77
- * @returns The instance for chaining
171
+ * Sets request mode - supports both direct call and fluent API
172
+ * @example
173
+ * request.withMode("cors")
174
+ * request.withMode(RequestMode.CORS)
175
+ * request.withMode.CORS()
78
176
  */
79
- withMode(mode: RequestMode): this;
177
+ get withMode(): ((mode: RequestMode | string) => BaseRequest) & {
178
+ CORS: () => BaseRequest;
179
+ NO_CORS: () => BaseRequest;
180
+ SAME_ORIGIN: () => BaseRequest;
181
+ NAVIGATE: () => BaseRequest;
182
+ };
80
183
  /**
81
184
  * Shorthand for setting the Content-Type header
82
185
  * @param contentType - The content type
@@ -107,9 +210,13 @@ export declare abstract class BaseRequest {
107
210
  * @returns The instance for chaining
108
211
  */
109
212
  withBearerToken(token: string): this;
213
+ /**
214
+ * Safely get headers as a Record<string, string>
215
+ * @returns The headers object
216
+ */
217
+ private getHeadersRecord;
110
218
  /**
111
219
  * Helper function to check for header presence in a case-insensitive way
112
- * @param headers Headers object
113
220
  * @param headerName Header name to check
114
221
  * @returns Boolean indicating if the header exists (case-insensitive)
115
222
  */
@@ -146,16 +253,113 @@ export declare abstract class BaseRequest {
146
253
  */
147
254
  withAntiCsrfHeaders(): this;
148
255
  /**
149
- * Enables caching for the request with the specified options
150
- * @param options The cache configuration options
256
+ * Add a request interceptor for this specific request
257
+ * Request interceptors can modify the request configuration or return an early response
258
+ *
259
+ * @param interceptor - The request interceptor function
151
260
  * @returns The instance for chaining
261
+ *
262
+ * @example
263
+ * request.withRequestInterceptor((config) => {
264
+ * config.headers['X-Custom'] = 'value';
265
+ * return config;
266
+ * });
267
+ */
268
+ withRequestInterceptor(interceptor: RequestInterceptor): this;
269
+ /**
270
+ * Add a response interceptor for this specific request
271
+ * Response interceptors can transform the response
272
+ *
273
+ * @param interceptor - The response interceptor function
274
+ * @returns The instance for chaining
275
+ *
276
+ * @example
277
+ * request.withResponseInterceptor((response) => {
278
+ * console.log('Status:', response.status);
279
+ * return response;
280
+ * });
281
+ */
282
+ withResponseInterceptor(interceptor: ResponseInterceptor): this;
283
+ /**
284
+ * Add an error interceptor for this specific request
285
+ * Error interceptors can handle or transform errors
286
+ *
287
+ * @param interceptor - The error interceptor function
288
+ * @returns The instance for chaining
289
+ *
290
+ * @example
291
+ * request.withErrorInterceptor((error) => {
292
+ * console.error('Request failed:', error);
293
+ * throw error;
294
+ * });
295
+ */
296
+ withErrorInterceptor(interceptor: ErrorInterceptor): this;
297
+ /**
298
+ * Execute the request and return the ResponseWrapper
299
+ * This is the base method for getting the full response.
300
+ *
301
+ * @returns A promise that resolves to the ResponseWrapper
302
+ *
303
+ * @example
304
+ * const response = await request.getResponse();
305
+ * console.log(response.status);
152
306
  */
153
- withCache(options?: CacheOptions): this;
307
+ getResponse(): Promise<ResponseWrapper>;
154
308
  /**
155
- * Send the request to the specified URL
156
- * @param url The URL to send the request to
309
+ * Execute the request and parse the response as JSON
310
+ *
311
+ * @returns A promise that resolves to the parsed JSON data
312
+ *
313
+ * @example
314
+ * const users = await request.getJson<User[]>();
157
315
  */
158
- sendTo(url: string): ResponsePromise;
316
+ getJson<T = unknown>(): Promise<T>;
317
+ /**
318
+ * Execute the request and get the response as text
319
+ *
320
+ * @returns A promise that resolves to the response text
321
+ *
322
+ * @example
323
+ * const text = await request.getText();
324
+ */
325
+ getText(): Promise<string>;
326
+ /**
327
+ * Execute the request and get the response as a Blob
328
+ *
329
+ * @returns A promise that resolves to the response Blob
330
+ *
331
+ * @example
332
+ * const blob = await request.getBlob();
333
+ */
334
+ getBlob(): Promise<Blob>;
335
+ /**
336
+ * Execute the request and get the response body as a ReadableStream
337
+ *
338
+ * @returns A promise that resolves to the response body stream
339
+ *
340
+ * @example
341
+ * const stream = await request.getBody();
342
+ */
343
+ getBody(): Promise<ReadableStream<Uint8Array> | null>;
344
+ /**
345
+ * Execute the request and extract specific data using a selector function
346
+ * If no selector is provided, returns the full JSON response.
347
+ *
348
+ * @param selector - Optional function to extract and transform data
349
+ * @returns A promise that resolves to the selected data
350
+ *
351
+ * @example
352
+ * // Get full response
353
+ * const data = await request.getData();
354
+ *
355
+ * // Extract specific data
356
+ * const users = await request.getData(data => data.results.users);
357
+ */
358
+ getData<T = unknown, R = T>(selector?: (data: T) => R): Promise<T | R>;
359
+ /**
360
+ * Apply CSRF protection headers based on configuration
361
+ */
362
+ private applyCsrfProtection;
159
363
  /**
160
364
  * Formats the URL with any query parameters
161
365
  * @param url The base URL
@@ -167,14 +371,44 @@ export declare abstract class BaseRequest {
167
371
  * @param url The formatted URL to send the request to
168
372
  * @param fetchOptions The fetch options to use
169
373
  * @returns A wrapped response object
374
+ * @throws RequestError if the request fails after all retries
170
375
  */
171
376
  private executeWithRetries;
172
- private executeRequest;
173
377
  /**
174
- * Executes a request using cache when possible
175
- * @param url The formatted URL to send the request to
176
- * @param fetchOptions The fetch options to use
177
- * @param cacheKey The cache key for this request
378
+ * Run request interceptors in order: global interceptors first, then per-request
379
+ * @param configParam - The request configuration
380
+ * @returns Modified config or a Response to short-circuit
178
381
  */
179
- private executeWithCache;
382
+ private runRequestInterceptors;
383
+ /**
384
+ * Run response interceptors in reverse order: per-request interceptors first, then global in reverse
385
+ * @param response - The response wrapper
386
+ * @returns Modified response wrapper
387
+ */
388
+ private runResponseInterceptors;
389
+ /**
390
+ * Run error interceptors in reverse order: per-request interceptors first, then global in reverse
391
+ * @param error - The error that occurred
392
+ * @returns Modified error or a ResponseWrapper to recover
393
+ */
394
+ private runErrorInterceptors;
395
+ /**
396
+ * Helper to create an abort signal with timeout support
397
+ * Handles various AbortSignal API levels gracefully
398
+ */
399
+ private createAbortSignal;
400
+ /**
401
+ * Manually combine two abort signals for older environments
402
+ * Returns the first signal and listens to the second
403
+ */
404
+ private combineSignalsManually;
405
+ /**
406
+ * Convert fetchOptions to RequestConfig with proper typing
407
+ */
408
+ private createRequestConfig;
409
+ /**
410
+ * Apply interceptor results back to fetchOptions
411
+ */
412
+ private applyRequestConfig;
413
+ private executeRequest;
180
414
  }
@@ -1,20 +1,43 @@
1
- import { BaseRequest } from "./BaseRequest";
2
- import type { Body } from "./types";
1
+ import { BaseRequest } from "./BaseRequest.js";
2
+ import type { Body } from "./types.js";
3
+ import type { ResponseWrapper } from "./ResponseWrapper.js";
3
4
  /**
4
5
  * Base class for requests that can have a body (POST, PUT, PATCH)
5
6
  */
6
7
  export declare abstract class BodyRequest extends BaseRequest {
7
8
  protected body?: Body;
8
9
  private bodyType?;
10
+ constructor(url: string);
9
11
  /**
10
12
  * Sets the body of the request
11
13
  * @param body - The request body
12
14
  */
13
15
  withBody(body: Body): this;
16
+ /**
17
+ * Sets a GraphQL query or mutation as the request body
18
+ * Automatically formats the body as JSON and sets Content-Type to application/json
19
+ *
20
+ * @param query - The GraphQL query or mutation string
21
+ * @param variables - Optional variables object to pass with the query
22
+ * @returns The request instance for chaining
23
+ *
24
+ * @example
25
+ * const request = new PostRequest('/graphql')
26
+ * .withGraphQL('query { user(id: $id) { name email } }', { id: '123' });
27
+ *
28
+ * @example
29
+ * const request = new PostRequest('/graphql')
30
+ * .withGraphQL('mutation { createUser(name: $name) { id } }', { name: 'John' });
31
+ */
32
+ withGraphQL(query: string, variables?: Record<string, unknown>): this;
33
+ /**
34
+ * Check if Content-Type header is already set (case-insensitive)
35
+ */
36
+ private hasContentType;
14
37
  private setContentTypeIfNeeded;
15
38
  /**
16
- * Send the request to the specified URL
39
+ * Execute the request and return the ResponseWrapper
17
40
  * Overrides the base implementation to add body handling
18
41
  */
19
- sendTo(url: string): ReturnType<BaseRequest["sendTo"]>;
42
+ getResponse(): Promise<ResponseWrapper>;
20
43
  }
@@ -3,11 +3,13 @@ export declare class RequestError extends Error {
3
3
  readonly response?: Response;
4
4
  readonly url: string;
5
5
  readonly method: string;
6
- readonly timeoutError?: boolean;
6
+ readonly isTimeout?: boolean;
7
+ readonly isAborted?: boolean;
7
8
  constructor(message: string, url: string, method: string, options?: {
8
9
  status?: number;
9
10
  response?: Response;
10
- timeoutError?: boolean;
11
+ isTimeout?: boolean;
12
+ isAborted?: boolean;
11
13
  });
12
14
  /**
13
15
  * Static methods for creating specific types of RequestError
@@ -15,4 +17,5 @@ export declare class RequestError extends Error {
15
17
  static timeout(url: string, method: string, timeoutMs: number): RequestError;
16
18
  static fromResponse(response: Response, url: string, method: string): RequestError;
17
19
  static networkError(url: string, method: string, originalError: Error): RequestError;
20
+ static abortError(url: string, method: string): RequestError;
18
21
  }
@@ -1,33 +1,86 @@
1
1
  /**
2
2
  * Wrapper for HTTP responses with methods to transform the response data
3
+ * Provides caching and conversion between different response formats.
3
4
  */
4
5
  export declare class ResponseWrapper {
5
6
  private readonly response;
7
+ readonly url?: string;
8
+ readonly method?: string;
6
9
  private textCache;
7
10
  private jsonCache;
8
11
  private blobCache;
9
- private arrayBufferCache;
10
12
  private bodyUsed;
11
- constructor(response: Response);
13
+ constructor(response: Response, url?: string, method?: string);
12
14
  get status(): number;
13
15
  get statusText(): string;
14
16
  get headers(): Headers;
15
17
  get ok(): boolean;
16
18
  get raw(): Response;
19
+ /**
20
+ * Parse the response body as JSON
21
+ * Caches the result for subsequent calls.
22
+ *
23
+ * @returns The parsed JSON data
24
+ * @throws Error if the response has already been consumed in a different format
25
+ *
26
+ * @example
27
+ * const data = await response.getJson();
28
+ * console.log(data.items);
29
+ */
17
30
  getJson<T = unknown>(): Promise<T>;
31
+ /**
32
+ * Get the response body as text
33
+ * Caches the result for subsequent calls. Attempts to convert from JSON or Blob
34
+ * if the body has already been consumed in those formats.
35
+ *
36
+ * @returns The response text
37
+ * @throws Error if the response has already been consumed in an incompatible format
38
+ *
39
+ * @example
40
+ * const text = await response.getText();
41
+ */
18
42
  getText(): Promise<string>;
43
+ /**
44
+ * Get the response body as a Blob
45
+ * Caches the result for subsequent calls. Attempts to convert from text
46
+ * if the body has already been consumed as text.
47
+ *
48
+ * @returns The response as a Blob
49
+ * @throws Error if the response has already been consumed in an incompatible format
50
+ *
51
+ * @example
52
+ * const blob = await response.getBlob();
53
+ * const url = URL.createObjectURL(blob);
54
+ */
19
55
  getBlob(): Promise<Blob>;
20
- getArrayBuffer(): Promise<ArrayBuffer>;
56
+ /**
57
+ * Get the raw response body as a ReadableStream
58
+ * This method consumes the body and should only be called once.
59
+ *
60
+ * @returns The response body as a ReadableStream or null
61
+ * @throws Error if the response body has already been consumed
62
+ *
63
+ * @example
64
+ * const stream = response.getBody();
65
+ * if (stream) {
66
+ * const reader = stream.getReader();
67
+ * // Process the stream
68
+ * }
69
+ */
21
70
  getBody(): ReadableStream<Uint8Array> | null;
22
- }
23
- /**
24
- * Type for Promise with response data transformation methods
25
- */
26
- export interface ResponsePromise<T = ResponseWrapper> extends Promise<T> {
27
- getJson<R = unknown>(): Promise<R>;
28
- getText(): Promise<string>;
29
- getBlob(): Promise<Blob>;
30
- getArrayBuffer(): Promise<ArrayBuffer>;
31
- getBody(): Promise<ReadableStream<Uint8Array> | null>;
71
+ /**
72
+ * Extract specific data using a selector function
73
+ * If no selector is provided, returns the full JSON response.
74
+ *
75
+ * @param selector - Optional function to extract and transform data
76
+ * @returns A promise that resolves to the selected data
77
+ *
78
+ * @example
79
+ * // Get full response
80
+ * const data = await response.getData();
81
+ *
82
+ * // Extract specific data
83
+ * const users = await response.getData(data => data.results.users);
84
+ */
32
85
  getData<T = unknown, R = T>(selector?: (data: T) => R): Promise<T | R>;
33
86
  }
@@ -43,17 +43,6 @@ export declare enum RedirectMode {
43
43
  FOLLOW = "follow",
44
44
  MANUAL = "manual"
45
45
  }
46
- /**
47
- * Enum for cache modes
48
- */
49
- export declare enum CacheMode {
50
- DEFAULT = "default",
51
- RELOAD = "reload",
52
- NO_STORE = "no-store",
53
- NO_CACHE = "no-cache",
54
- FORCE_CACHE = "force-cache",
55
- ONLY_IF_CACHED = "only-if-cached"
56
- }
57
46
  /**
58
47
  * Enum for cookie SameSite policies
59
48
  */