@ahoo-wang/fetcher 0.9.1 → 0.9.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
@@ -8,12 +8,13 @@
8
8
  [![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40ahoo-wang%2Ffetcher)](https://www.npmjs.com/package/@ahoo-wang/fetcher)
9
9
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Ahoo-Wang/fetcher)
10
10
 
11
- A modern, ultra-lightweight (1.9kB) HTTP client with built-in path parameters, query parameters, and Axios-like API. 86%
11
+ A modern, ultra-lightweight (2.3KiB) HTTP client with built-in path parameters, query parameters, and Axios-like API.
12
+ 83%
12
13
  smaller than Axios while providing the same powerful features.
13
14
 
14
15
  ## 🌟 Features
15
16
 
16
- - **⚡ Ultra-Lightweight**: Only 1.9kB min+gzip - 86% smaller than Axios
17
+ - **⚡ Ultra-Lightweight**: Only 2.3KiB min+gzip - 83% smaller than Axios
17
18
  - **🧭 Path & Query Parameters**: Built-in support for path (`{id}`) and query parameters
18
19
  - **🔗 Interceptor System**: Request, response, and error interceptors for middleware patterns
19
20
  - **⏱️ Timeout Control**: Configurable request timeouts with proper error handling
package/README.zh-CN.md CHANGED
@@ -8,11 +8,11 @@
8
8
  [![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40ahoo-wang%2Ffetcher)](https://www.npmjs.com/package/@ahoo-wang/fetcher)
9
9
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Ahoo-Wang/fetcher)
10
10
 
11
- 一个现代、超轻量级(1.9kB)的 HTTP 客户端,内置路径参数、查询参数和类似 Axios 的 API。比 Axios 小 86%,同时提供相同的强大功能。
11
+ 一个现代、超轻量级(2.3kB)的 HTTP 客户端,内置路径参数、查询参数和类似 Axios 的 API。比 Axios 小 83%,同时提供相同的强大功能。
12
12
 
13
13
  ## 🌟 特性
14
14
 
15
- - **⚡ 超轻量级**:仅 1.9kB min+gzip - 比 Axios 小 86%
15
+ - **⚡ 超轻量级**:仅 2.3KiB min+gzip - 比 Axios 小 83%
16
16
  - **🧭 路径和查询参数**:内置支持路径(`{id}`)和查询参数
17
17
  - **🔗 拦截器系统**:请求、响应和错误拦截器的中间件模式
18
18
  - **⏱️ 超时控制**:可配置的请求超时和适当的错误处理
package/dist/fetcher.d.ts CHANGED
@@ -65,7 +65,7 @@ export declare class Fetcher implements UrlBuilderCapable, RequestHeadersCapable
65
65
  * @param url - The URL path for the request (relative to baseURL if set)
66
66
  * @param request - Request configuration including headers, body, parameters, etc.
67
67
  * @returns Promise that resolves to the HTTP response
68
- * @throws Error if the request fails and no response is generated
68
+ * @throws FetchError if the request fails and no response is generated
69
69
  */
70
70
  fetch(url: string, request?: FetchRequestInit): Promise<Response>;
71
71
  /**
@@ -81,20 +81,90 @@ export declare class Fetcher implements UrlBuilderCapable, RequestHeadersCapable
81
81
  */
82
82
  request(request: FetchRequest): Promise<FetchExchange>;
83
83
  /**
84
- * Processes a FetchExchange through the interceptor chain.
84
+ * Processes a FetchExchange through the interceptor pipeline.
85
85
  *
86
- * Orchestrates the complete request lifecycle by applying interceptors in sequence:
87
- * 1. Request interceptors - Modify the outgoing request
88
- * 2. Response interceptors - Process the incoming response
86
+ * This method is the core of the Fetcher's interceptor system. It executes the three
87
+ * phases of interceptors in sequence:
88
+ * 1. Request interceptors - Process the request before sending
89
+ * 2. Response interceptors - Process the response after receiving
90
+ * 3. Error interceptors - Handle any errors that occurred during the process
89
91
  *
90
- * Error handling follows this flow:
91
- * - If an error occurs, error interceptors are invoked
92
- * - If an error interceptor produces a response, it's returned
93
- * - Otherwise, the original error is re-thrown
92
+ * The interceptor pipeline follows the Chain of Responsibility pattern, where each
93
+ * interceptor can modify the exchange object and decide whether to continue or
94
+ * terminate the chain.
94
95
  *
95
- * @param fetchExchange - The exchange object containing request and response data
96
- * @returns Promise resolving to the processed exchange
97
- * @throws Error if an unhandled error occurs during processing
96
+ * @param fetchExchange - The exchange object containing request, response, and error information
97
+ * @returns Promise that resolves to the processed FetchExchange
98
+ * @throws ExchangeError if an unhandled error occurs during processing
99
+ *
100
+ * @remarks
101
+ * The method handles three distinct phases:
102
+ *
103
+ * 1. Request Phase: Executes request interceptors which can modify headers, URL, body, etc.
104
+ * Built-in interceptors handle URL resolution, body serialization, and actual HTTP execution.
105
+ *
106
+ * 2. Response Phase: Executes response interceptors which can transform or validate responses.
107
+ * These interceptors only run if the request phase completed without throwing.
108
+ *
109
+ * 3. Error Phase: Executes error interceptors when any phase throws an error. Error interceptors
110
+ * can handle errors by clearing the error property. If error interceptors clear the error,
111
+ * the exchange is returned successfully.
112
+ *
113
+ * Error Handling:
114
+ * - If any interceptor throws an error, the error phase is triggered
115
+ * - Error interceptors can "fix" errors by clearing the error property on the exchange
116
+ * - If errors remain after error interceptors run, they are wrapped in ExchangeError
117
+ *
118
+ * Order of Execution:
119
+ * 1. Request interceptors (sorted by order property, ascending)
120
+ * 2. Response interceptors (sorted by order property, ascending) - only if no error in request phase
121
+ * 3. Error interceptors (sorted by order property, ascending) - only if an error occurred
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * // Create a fetcher with custom interceptors
126
+ * const fetcher = new Fetcher();
127
+ *
128
+ * // Add a request interceptor
129
+ * fetcher.interceptors.request.use({
130
+ * name: 'AuthInterceptor',
131
+ * order: 100,
132
+ * async intercept(exchange: FetchExchange) {
133
+ * exchange.request.headers = {
134
+ * ...exchange.request.headers,
135
+ * 'Authorization': 'Bearer ' + getToken()
136
+ * };
137
+ * }
138
+ * });
139
+ *
140
+ * // Add a response interceptor
141
+ * fetcher.interceptors.response.use({
142
+ * name: 'ResponseLogger',
143
+ * order: 100,
144
+ * async intercept(exchange: FetchExchange) {
145
+ * console.log(`Response status: ${exchange.response?.status}`);
146
+ * }
147
+ * });
148
+ *
149
+ * // Add an error interceptor
150
+ * fetcher.interceptors.error.use({
151
+ * name: 'ErrorLogger',
152
+ * order: 100,
153
+ * async intercept(exchange: FetchExchange) {
154
+ * console.error(`Request to ${exchange.request.url} failed:`, exchange.error);
155
+ * // Clear the error to indicate it's been handled
156
+ * exchange.error = undefined;
157
+ * }
158
+ * });
159
+ *
160
+ * // Create and process an exchange
161
+ * const request: FetchRequest = {
162
+ * url: '/api/users',
163
+ * method: HttpMethod.GET
164
+ * };
165
+ * const exchange = new FetchExchange(fetcher, request);
166
+ * const result = await fetcher.exchange(exchange);
167
+ * ```
98
168
  */
99
169
  exchange(fetchExchange: FetchExchange): Promise<FetchExchange>;
100
170
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../src/fetcher.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAkB,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,cAAc,EAEd,YAAY,EACZ,gBAAgB,EAEhB,cAAc,EACd,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AAGxB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,cACf,SAAQ,cAAc,EACpB,qBAAqB,EACrB,cAAc;IAChB,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAMD,eAAO,MAAM,eAAe,EAAE,cAG7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,OACX,YAAW,iBAAiB,EAAE,qBAAqB,EAAE,cAAc;IAEnE,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,cAAc,CAAmB;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,mBAAmB,CAAC;IAElC;;;;;;;OAOG;gBACS,OAAO,GAAE,cAAgC;IAOrD;;;;;;;;;;OAUG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAU3E;;;;;;;;;;OAUG;IACG,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAa5D;;;;;;;;;;;;;;;OAeG;IACG,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAkBpE;;;;;;;;;;OAUG;YACW,WAAW;IAWzB;;;;;;;;;OASG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,MAAM,CAAM,GACtD,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;OAQG;IACG,IAAI,CACR,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;OAQG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;OAQG;IACG,MAAM,CACV,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;OAQG;IACG,KAAK,CACT,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;;OASG;IACG,IAAI,CACR,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,MAAM,CAAM,GACtD,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;;OASG;IACG,OAAO,CACX,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,MAAM,CAAM,GACtD,OAAO,CAAC,QAAQ,CAAC;CAGrB"}
1
+ {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../src/fetcher.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAkB,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,cAAc,EAEd,YAAY,EACZ,gBAAgB,EAEhB,cAAc,EACd,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AAIxB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,cACf,SAAQ,cAAc,EACpB,qBAAqB,EACrB,cAAc;IAChB,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAMD,eAAO,MAAM,eAAe,EAAE,cAG7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,OACX,YAAW,iBAAiB,EAAE,qBAAqB,EAAE,cAAc;IACnE,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,cAAc,CAAmB;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,mBAAmB,CAAC;IAElC;;;;;;;OAOG;gBACS,OAAO,GAAE,cAAgC;IAOrD;;;;;;;;;;OAUG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAU3E;;;;;;;;;;OAUG;IACG,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAa5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqFG;IACG,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAsBpE;;;;;;;;;;OAUG;YACW,WAAW;IAWzB;;;;;;;;;OASG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,MAAM,CAAM,GACtD,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;OAQG;IACG,IAAI,CACR,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;OAQG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;OAQG;IACG,MAAM,CACV,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;OAQG;IACG,KAAK,CACT,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;;OASG;IACG,IAAI,CACR,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,MAAM,CAAM,GACtD,OAAO,CAAC,QAAQ,CAAC;IAIpB;;;;;;;;;OASG;IACG,OAAO,CACX,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,MAAM,CAAM,GACtD,OAAO,CAAC,QAAQ,CAAC;CAGrB"}
@@ -0,0 +1,122 @@
1
+ import { FetchExchange } from './fetchExchange';
2
+ import { FetchRequest } from './fetchRequest';
3
+ /**
4
+ * Base error class for all Fetcher-related errors.
5
+ *
6
+ * This class extends the native Error class and provides a foundation for
7
+ * all custom errors thrown by the Fetcher library. It includes support for
8
+ * error chaining through the cause property.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * try {
13
+ * await fetcher.get('/api/users');
14
+ * } catch (error) {
15
+ * if (error instanceof FetcherError) {
16
+ * console.log('Fetcher error:', error.message);
17
+ * if (error.cause) {
18
+ * console.log('Caused by:', error.cause);
19
+ * }
20
+ * }
21
+ * }
22
+ * ```
23
+ */
24
+ export declare class FetcherError extends Error {
25
+ readonly cause?: (Error | any) | undefined;
26
+ /**
27
+ * Creates a new FetcherError instance.
28
+ *
29
+ * @param errorMsg - Optional error message. If not provided, will use the cause's message or a default message.
30
+ * @param cause - Optional underlying error that caused this error.
31
+ */
32
+ constructor(errorMsg?: string, cause?: (Error | any) | undefined);
33
+ }
34
+ /**
35
+ * Custom error class for FetchExchange related errors.
36
+ *
37
+ * This error is thrown when there are issues with the HTTP exchange process,
38
+ * such as when a request fails and no response is generated. It provides
39
+ * comprehensive information about the failed request through the exchange object.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * try {
44
+ * await fetcher.get('/api/users');
45
+ * } catch (error) {
46
+ * if (error instanceof ExchangeError) {
47
+ * console.log('Request URL:', error.exchange.request.url);
48
+ * console.log('Request method:', error.exchange.request.method);
49
+ * if (error.exchange.error) {
50
+ * console.log('Underlying error:', error.exchange.error);
51
+ * }
52
+ * }
53
+ * }
54
+ * ```
55
+ */
56
+ export declare class ExchangeError extends FetcherError {
57
+ readonly exchange: FetchExchange;
58
+ /**
59
+ * Creates a new ExchangeError instance.
60
+ *
61
+ * @param exchange - The FetchExchange object containing request/response/error information.
62
+ */
63
+ constructor(exchange: FetchExchange);
64
+ }
65
+ /**
66
+ * Custom error class for Fetcher request failures.
67
+ *
68
+ * This error is thrown when a fetch request fails and no response is generated.
69
+ * It wraps the FetchExchange object to provide comprehensive information about the failed request.
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * try {
74
+ * await fetcher.get('/api/users');
75
+ * } catch (error) {
76
+ * if (error instanceof FetchError) {
77
+ * console.log('Failed URL:', error.exchange.request.url);
78
+ * console.log('Request headers:', error.exchange.request.headers);
79
+ * console.log('Interceptor attributes:', error.exchange.attributes);
80
+ * }
81
+ * }
82
+ * ```
83
+ */
84
+ export declare class FetchError extends FetcherError {
85
+ readonly exchange: FetchExchange;
86
+ /**
87
+ * Creates a new FetchError instance.
88
+ *
89
+ * @param exchange - The FetchExchange object containing request/response/error information.
90
+ * @param errorMsg - Optional custom error message. If not provided, will use the exchange's error message.
91
+ */
92
+ constructor(exchange: FetchExchange, errorMsg?: string);
93
+ }
94
+ /**
95
+ * Exception class thrown when an HTTP request times out.
96
+ *
97
+ * This error is thrown by the timeoutFetch function when a request exceeds its timeout limit.
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * try {
102
+ * const response = await timeoutFetch('https://api.example.com/users', {}, 1000);
103
+ * } catch (error) {
104
+ * if (error instanceof FetchTimeoutError) {
105
+ * console.log(`Request timed out after ${error.timeout}ms`);
106
+ * }
107
+ * }
108
+ * ```
109
+ */
110
+ export declare class FetchTimeoutError extends FetcherError {
111
+ /**
112
+ * The request options that timed out.
113
+ */
114
+ request: FetchRequest;
115
+ /**
116
+ * Creates a new FetchTimeoutError instance.
117
+ *
118
+ * @param request - The request options that timed out
119
+ */
120
+ constructor(request: FetchRequest);
121
+ }
122
+ //# sourceMappingURL=fetcherError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetcherError.d.ts","sourceRoot":"","sources":["../src/fetcherError.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,YAAa,SAAQ,KAAK;aASnB,KAAK,CAAC,GAAE,KAAK,GAAG,GAAG;IARrC;;;;;OAKG;gBAED,QAAQ,CAAC,EAAE,MAAM,EACD,KAAK,CAAC,GAAE,KAAK,GAAG,GAAG,aAAA;CAetC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,aAAc,SAAQ,YAAY;aAMjB,QAAQ,EAAE,aAAa;IALnD;;;;OAIG;gBACyB,QAAQ,EAAE,aAAa;CASpD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,UAAW,SAAQ,YAAY;aAQxB,QAAQ,EAAE,aAAa;IAPzC;;;;;OAKG;gBAEe,QAAQ,EAAE,aAAa,EACvC,QAAQ,CAAC,EAAE,MAAM;CAUpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,iBAAkB,SAAQ,YAAY;IACjD;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;;;OAIG;gBACS,OAAO,EAAE,YAAY;CASlC"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './fetcherError';
1
2
  export * from './fetcher';
2
3
  export * from './fetcherRegistrar';
3
4
  export * from './fetchExchange';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC"}
package/dist/index.es.js CHANGED
@@ -1,10 +1,56 @@
1
- function I(r) {
1
+ class a extends Error {
2
+ /**
3
+ * Creates a new FetcherError instance.
4
+ *
5
+ * @param errorMsg - Optional error message. If not provided, will use the cause's message or a default message.
6
+ * @param cause - Optional underlying error that caused this error.
7
+ */
8
+ constructor(e, t) {
9
+ const s = e || t?.message || "An error occurred in the fetcher";
10
+ super(s), this.cause = t, this.name = "FetcherError", t?.stack && (this.stack = t.stack), Object.setPrototypeOf(this, a.prototype);
11
+ }
12
+ }
13
+ class f extends a {
14
+ /**
15
+ * Creates a new ExchangeError instance.
16
+ *
17
+ * @param exchange - The FetchExchange object containing request/response/error information.
18
+ */
19
+ constructor(e) {
20
+ const t = e.error?.message || e.response?.statusText || `Request to ${e.request.url} failed during exchange`;
21
+ super(t, e.error), this.exchange = e, this.name = "ExchangeError", Object.setPrototypeOf(this, f.prototype);
22
+ }
23
+ }
24
+ class y extends a {
25
+ /**
26
+ * Creates a new FetchError instance.
27
+ *
28
+ * @param exchange - The FetchExchange object containing request/response/error information.
29
+ * @param errorMsg - Optional custom error message. If not provided, will use the exchange's error message.
30
+ */
31
+ constructor(e, t) {
32
+ const s = t || e.error?.message || `Request to ${e.request.url} failed with no response`;
33
+ super(s, e.error), this.exchange = e, this.name = "FetchError", Object.setPrototypeOf(this, y.prototype);
34
+ }
35
+ }
36
+ class E extends a {
37
+ /**
38
+ * Creates a new FetchTimeoutError instance.
39
+ *
40
+ * @param request - The request options that timed out
41
+ */
42
+ constructor(e) {
43
+ const t = e.method || "GET", s = `Request timeout of ${e.timeout}ms exceeded for ${t} ${e.url}`;
44
+ super(s), this.name = "FetchTimeoutError", this.request = e, Object.setPrototypeOf(this, E.prototype);
45
+ }
46
+ }
47
+ function w(r) {
2
48
  return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(r);
3
49
  }
4
- function b(r, e) {
5
- return I(e) ? e : e ? r.replace(/\/?\/$/, "") + "/" + e.replace(/^\/+/, "") : r;
50
+ function P(r, e) {
51
+ return w(e) ? e : e ? r.replace(/\/?\/$/, "") + "/" + e.replace(/^\/+/, "") : r;
6
52
  }
7
- class g {
53
+ class R {
8
54
  /**
9
55
  * Initializes a new UrlBuilder instance.
10
56
  *
@@ -37,13 +83,13 @@ class g {
37
83
  * ```
38
84
  */
39
85
  build(e, t) {
40
- const s = t?.path, n = t?.query, i = b(this.baseURL, e);
41
- let o = this.interpolateUrl(i, s);
42
- if (n) {
43
- const u = new URLSearchParams(n).toString();
44
- u && (o += "?" + u);
86
+ const s = t?.path, o = t?.query, i = P(this.baseURL, e);
87
+ let n = this.interpolateUrl(i, s);
88
+ if (o) {
89
+ const u = new URLSearchParams(o).toString();
90
+ u && (n += "?" + u);
45
91
  }
46
- return o;
92
+ return n;
47
93
  }
48
94
  /**
49
95
  * Resolves a complete URL from a FetchRequest.
@@ -85,57 +131,46 @@ class g {
85
131
  * ```
86
132
  */
87
133
  interpolateUrl(e, t) {
88
- return t ? e.replace(/{([^}]+)}/g, (s, n) => {
89
- const i = t[n];
134
+ return t ? e.replace(/{([^}]+)}/g, (s, o) => {
135
+ const i = t[o];
90
136
  if (i === void 0)
91
- throw new Error(`Missing required path parameter: ${n}`);
137
+ throw new Error(`Missing required path parameter: ${o}`);
92
138
  return String(i);
93
139
  }) : e;
94
140
  }
95
141
  }
96
- function w(r, e) {
142
+ function F(r, e) {
97
143
  return typeof r < "u" ? r : e;
98
144
  }
99
- class p extends Error {
100
- /**
101
- * Creates a new FetchTimeoutError instance.
102
- *
103
- * @param request - The request options that timed out
104
- */
105
- constructor(e) {
106
- const t = e.method || "GET", s = `Request timeout of ${e.timeout}ms exceeded for ${t} ${e.url}`;
107
- super(s), this.name = "FetchTimeoutError", this.request = e, Object.setPrototypeOf(this, p.prototype);
108
- }
109
- }
110
- async function P(r) {
145
+ async function A(r) {
111
146
  const e = r.url, t = r.timeout, s = r;
112
147
  if (!t)
113
148
  return fetch(e, s);
114
- const n = new AbortController(), i = {
149
+ const o = new AbortController(), i = {
115
150
  ...s,
116
- signal: n.signal
151
+ signal: o.signal
117
152
  };
118
- let o = null;
119
- const u = new Promise((L, E) => {
120
- o = setTimeout(() => {
121
- o && clearTimeout(o);
122
- const y = new p(r);
123
- n.abort(y), E(y);
153
+ let n = null;
154
+ const u = new Promise((D, I) => {
155
+ n = setTimeout(() => {
156
+ n && clearTimeout(n);
157
+ const b = new E(r);
158
+ o.abort(b), I(b);
124
159
  }, t);
125
160
  });
126
161
  try {
127
162
  return await Promise.race([fetch(e, i), u]);
128
163
  } finally {
129
- o && clearTimeout(o);
164
+ n && clearTimeout(n);
130
165
  }
131
166
  }
132
- function a(r, e) {
167
+ function h(r, e) {
133
168
  return e ? r.filter(e).sort((t, s) => t.order - s.order) : [...r].sort((t, s) => t.order - s.order);
134
169
  }
135
170
  var c = /* @__PURE__ */ ((r) => (r.GET = "GET", r.POST = "POST", r.PUT = "PUT", r.DELETE = "DELETE", r.PATCH = "PATCH", r.HEAD = "HEAD", r.OPTIONS = "OPTIONS", r))(c || {});
136
- const v = "Content-Type";
137
- var f = /* @__PURE__ */ ((r) => (r.APPLICATION_JSON = "application/json", r.TEXT_EVENT_STREAM = "text/event-stream", r))(f || {});
138
- class R {
171
+ const G = "Content-Type";
172
+ var T = /* @__PURE__ */ ((r) => (r.APPLICATION_JSON = "application/json", r.TEXT_EVENT_STREAM = "text/event-stream", r))(T || {});
173
+ class O {
139
174
  constructor() {
140
175
  this.name = "RequestBodyInterceptor", this.order = Number.MIN_SAFE_INTEGER + 200;
141
176
  }
@@ -183,11 +218,11 @@ class R {
183
218
  return;
184
219
  const s = { ...t };
185
220
  s.body = JSON.stringify(t.body), s.headers || (s.headers = {});
186
- const n = s.headers;
187
- n["Content-Type"] || (n["Content-Type"] = f.APPLICATION_JSON), e.request = s;
221
+ const o = s.headers;
222
+ o["Content-Type"] || (o["Content-Type"] = T.APPLICATION_JSON), e.request = s;
188
223
  }
189
224
  }
190
- class F {
225
+ class q {
191
226
  constructor() {
192
227
  this.name = "FetchInterceptor", this.order = Number.MAX_SAFE_INTEGER - 100;
193
228
  }
@@ -217,10 +252,10 @@ class F {
217
252
  * console.log(exchange.response); // HTTP response object
218
253
  */
219
254
  async intercept(e) {
220
- e.response = await P(e.request);
255
+ e.response = await A(e.request);
221
256
  }
222
257
  }
223
- class A {
258
+ class N {
224
259
  constructor() {
225
260
  this.name = "UrlResolveInterceptor", this.order = Number.MIN_SAFE_INTEGER + 100;
226
261
  }
@@ -234,7 +269,7 @@ class A {
234
269
  t.url = e.fetcher.urlBuilder.resolveRequestUrl(t);
235
270
  }
236
271
  }
237
- class h {
272
+ class d {
238
273
  /**
239
274
  * Initializes a new InterceptorManager instance.
240
275
  *
@@ -245,7 +280,7 @@ class h {
245
280
  * upon construction.
246
281
  */
247
282
  constructor(e = []) {
248
- this.sortedInterceptors = [], this.sortedInterceptors = a(e);
283
+ this.sortedInterceptors = [], this.sortedInterceptors = h(e);
249
284
  }
250
285
  /**
251
286
  * Gets the name of this interceptor manager.
@@ -277,7 +312,7 @@ class h {
277
312
  * After adding, interceptors are automatically sorted by their order property.
278
313
  */
279
314
  use(e) {
280
- return this.sortedInterceptors.some((t) => t.name === e.name) ? !1 : (this.sortedInterceptors = a([
315
+ return this.sortedInterceptors.some((t) => t.name === e.name) ? !1 : (this.sortedInterceptors = h([
281
316
  ...this.sortedInterceptors,
282
317
  e
283
318
  ]), !0);
@@ -291,7 +326,7 @@ class h {
291
326
  */
292
327
  eject(e) {
293
328
  const t = this.sortedInterceptors;
294
- return this.sortedInterceptors = a(
329
+ return this.sortedInterceptors = h(
295
330
  t,
296
331
  (s) => s.name !== e
297
332
  ), t.length !== this.sortedInterceptors.length;
@@ -321,18 +356,18 @@ class h {
321
356
  await t.intercept(e);
322
357
  }
323
358
  }
324
- class N {
359
+ class S {
325
360
  constructor() {
326
- this.request = new h([
327
- new A(),
328
- new R(),
329
- new F()
330
- ]), this.response = new h(), this.error = new h();
361
+ this.request = new d([
362
+ new N(),
363
+ new O(),
364
+ new q()
365
+ ]), this.response = new d(), this.error = new d();
331
366
  }
332
367
  }
333
- class S {
334
- constructor(e, t, s, n) {
335
- this.attributes = {}, this.fetcher = e, this.request = t, this.response = s, this.error = n;
368
+ class U {
369
+ constructor(e, t, s, o) {
370
+ this.attributes = {}, this.fetcher = e, this.request = t, this.response = s, this.error = o;
336
371
  }
337
372
  /**
338
373
  * Checks if the exchange has an error.
@@ -351,17 +386,17 @@ class S {
351
386
  return !!this.response;
352
387
  }
353
388
  }
354
- function d(r, e) {
389
+ function l(r, e) {
355
390
  if (!(r === void 0 && e === void 0))
356
391
  return e === void 0 ? r : r === void 0 ? e : { ...r, ...e };
357
392
  }
358
- const l = {
359
- "Content-Type": f.APPLICATION_JSON
360
- }, T = {
393
+ const p = {
394
+ "Content-Type": T.APPLICATION_JSON
395
+ }, g = {
361
396
  baseURL: "",
362
- headers: l
397
+ headers: p
363
398
  };
364
- class q {
399
+ class _ {
365
400
  /**
366
401
  * Initializes a new Fetcher instance with optional configuration.
367
402
  *
@@ -370,8 +405,8 @@ class q {
370
405
  *
371
406
  * @param options - Configuration options for the Fetcher instance
372
407
  */
373
- constructor(e = T) {
374
- this.headers = l, this.urlBuilder = new g(e.baseURL), this.headers = e.headers ?? l, this.timeout = e.timeout, this.interceptors = e.interceptors ?? new N();
408
+ constructor(e = g) {
409
+ this.headers = p, this.urlBuilder = new R(e.baseURL), this.headers = e.headers ?? p, this.timeout = e.timeout, this.interceptors = e.interceptors ?? new S();
375
410
  }
376
411
  /**
377
412
  * Executes an HTTP request with the specified URL and options.
@@ -382,15 +417,15 @@ class q {
382
417
  * @param url - The URL path for the request (relative to baseURL if set)
383
418
  * @param request - Request configuration including headers, body, parameters, etc.
384
419
  * @returns Promise that resolves to the HTTP response
385
- * @throws Error if the request fails and no response is generated
420
+ * @throws FetchError if the request fails and no response is generated
386
421
  */
387
422
  async fetch(e, t = {}) {
388
423
  const s = t;
389
424
  s.url = e;
390
- const n = await this.request(s);
391
- if (!n.response)
392
- throw new Error(`Request to ${s.url} failed with no response`);
393
- return n.response;
425
+ const o = await this.request(s);
426
+ if (!o.response)
427
+ throw new y(o, `Request to ${s.url} failed with no response`);
428
+ return o.response;
394
429
  }
395
430
  /**
396
431
  * Processes an HTTP request through the Fetcher's internal workflow.
@@ -404,36 +439,106 @@ class q {
404
439
  * @throws Error if an unhandled error occurs during request processing
405
440
  */
406
441
  async request(e) {
407
- const t = d(e.headers, this.headers), s = {
442
+ const t = l(e.headers, this.headers), s = {
408
443
  ...e,
409
444
  headers: t,
410
- timeout: w(e.timeout, this.timeout)
411
- }, n = new S(this, s);
412
- return this.exchange(n);
445
+ timeout: F(e.timeout, this.timeout)
446
+ }, o = new U(this, s);
447
+ return this.exchange(o);
413
448
  }
414
449
  /**
415
- * Processes a FetchExchange through the interceptor chain.
450
+ * Processes a FetchExchange through the interceptor pipeline.
451
+ *
452
+ * This method is the core of the Fetcher's interceptor system. It executes the three
453
+ * phases of interceptors in sequence:
454
+ * 1. Request interceptors - Process the request before sending
455
+ * 2. Response interceptors - Process the response after receiving
456
+ * 3. Error interceptors - Handle any errors that occurred during the process
457
+ *
458
+ * The interceptor pipeline follows the Chain of Responsibility pattern, where each
459
+ * interceptor can modify the exchange object and decide whether to continue or
460
+ * terminate the chain.
461
+ *
462
+ * @param fetchExchange - The exchange object containing request, response, and error information
463
+ * @returns Promise that resolves to the processed FetchExchange
464
+ * @throws ExchangeError if an unhandled error occurs during processing
465
+ *
466
+ * @remarks
467
+ * The method handles three distinct phases:
468
+ *
469
+ * 1. Request Phase: Executes request interceptors which can modify headers, URL, body, etc.
470
+ * Built-in interceptors handle URL resolution, body serialization, and actual HTTP execution.
471
+ *
472
+ * 2. Response Phase: Executes response interceptors which can transform or validate responses.
473
+ * These interceptors only run if the request phase completed without throwing.
416
474
  *
417
- * Orchestrates the complete request lifecycle by applying interceptors in sequence:
418
- * 1. Request interceptors - Modify the outgoing request
419
- * 2. Response interceptors - Process the incoming response
475
+ * 3. Error Phase: Executes error interceptors when any phase throws an error. Error interceptors
476
+ * can handle errors by clearing the error property. If error interceptors clear the error,
477
+ * the exchange is returned successfully.
420
478
  *
421
- * Error handling follows this flow:
422
- * - If an error occurs, error interceptors are invoked
423
- * - If an error interceptor produces a response, it's returned
424
- * - Otherwise, the original error is re-thrown
479
+ * Error Handling:
480
+ * - If any interceptor throws an error, the error phase is triggered
481
+ * - Error interceptors can "fix" errors by clearing the error property on the exchange
482
+ * - If errors remain after error interceptors run, they are wrapped in ExchangeError
425
483
  *
426
- * @param fetchExchange - The exchange object containing request and response data
427
- * @returns Promise resolving to the processed exchange
428
- * @throws Error if an unhandled error occurs during processing
484
+ * Order of Execution:
485
+ * 1. Request interceptors (sorted by order property, ascending)
486
+ * 2. Response interceptors (sorted by order property, ascending) - only if no error in request phase
487
+ * 3. Error interceptors (sorted by order property, ascending) - only if an error occurred
488
+ *
489
+ * @example
490
+ * ```typescript
491
+ * // Create a fetcher with custom interceptors
492
+ * const fetcher = new Fetcher();
493
+ *
494
+ * // Add a request interceptor
495
+ * fetcher.interceptors.request.use({
496
+ * name: 'AuthInterceptor',
497
+ * order: 100,
498
+ * async intercept(exchange: FetchExchange) {
499
+ * exchange.request.headers = {
500
+ * ...exchange.request.headers,
501
+ * 'Authorization': 'Bearer ' + getToken()
502
+ * };
503
+ * }
504
+ * });
505
+ *
506
+ * // Add a response interceptor
507
+ * fetcher.interceptors.response.use({
508
+ * name: 'ResponseLogger',
509
+ * order: 100,
510
+ * async intercept(exchange: FetchExchange) {
511
+ * console.log(`Response status: ${exchange.response?.status}`);
512
+ * }
513
+ * });
514
+ *
515
+ * // Add an error interceptor
516
+ * fetcher.interceptors.error.use({
517
+ * name: 'ErrorLogger',
518
+ * order: 100,
519
+ * async intercept(exchange: FetchExchange) {
520
+ * console.error(`Request to ${exchange.request.url} failed:`, exchange.error);
521
+ * // Clear the error to indicate it's been handled
522
+ * exchange.error = undefined;
523
+ * }
524
+ * });
525
+ *
526
+ * // Create and process an exchange
527
+ * const request: FetchRequest = {
528
+ * url: '/api/users',
529
+ * method: HttpMethod.GET
530
+ * };
531
+ * const exchange = new FetchExchange(fetcher, request);
532
+ * const result = await fetcher.exchange(exchange);
533
+ * ```
429
534
  */
430
535
  async exchange(e) {
431
536
  try {
432
537
  return await this.interceptors.request.intercept(e), await this.interceptors.response.intercept(e), e;
433
538
  } catch (t) {
434
- if (e.error = t, await this.interceptors.error.intercept(e), e.hasResponse())
539
+ if (e.error = t, await this.interceptors.error.intercept(e), !e.hasError())
435
540
  return e;
436
- throw e.error;
541
+ throw new f(e);
437
542
  }
438
543
  }
439
544
  /**
@@ -542,7 +647,7 @@ class q {
542
647
  }
543
648
  }
544
649
  const m = "default";
545
- class O {
650
+ class L {
546
651
  constructor() {
547
652
  this.registrar = /* @__PURE__ */ new Map();
548
653
  }
@@ -642,31 +747,31 @@ class O {
642
747
  return new Map(this.registrar);
643
748
  }
644
749
  }
645
- const U = new O();
646
- function C(r, e) {
750
+ const v = new L();
751
+ function j(r, e) {
647
752
  if (Object.keys(r).length === 0)
648
753
  return e;
649
754
  if (Object.keys(e).length === 0)
650
755
  return r;
651
756
  const t = {
652
- path: d(r.urlParams?.path, e.urlParams?.path),
653
- query: d(r.urlParams?.query, e.urlParams?.query)
757
+ path: l(r.urlParams?.path, e.urlParams?.path),
758
+ query: l(r.urlParams?.query, e.urlParams?.query)
654
759
  }, s = {
655
760
  ...r.headers,
656
761
  ...e.headers
657
- }, n = e.method ?? r.method, i = e.body ?? r.body, o = e.timeout ?? r.timeout, u = e.signal ?? r.signal;
762
+ }, o = e.method ?? r.method, i = e.body ?? r.body, n = e.timeout ?? r.timeout, u = e.signal ?? r.signal;
658
763
  return {
659
764
  ...r,
660
765
  ...e,
661
- method: n,
766
+ method: o,
662
767
  urlParams: t,
663
768
  headers: s,
664
769
  body: i,
665
- timeout: o,
770
+ timeout: n,
666
771
  signal: u
667
772
  };
668
773
  }
669
- class _ extends q {
774
+ class C extends _ {
670
775
  /**
671
776
  * Create a NamedFetcher instance and automatically register it with the global fetcherRegistrar
672
777
  *
@@ -684,35 +789,38 @@ class _ extends q {
684
789
  * headers: { 'Authorization': 'Bearer token' }
685
790
  * });
686
791
  */
687
- constructor(e, t = T) {
688
- super(t), this.name = e, U.register(e, this);
792
+ constructor(e, t = g) {
793
+ super(t), this.name = e, v.register(e, this);
689
794
  }
690
795
  }
691
- const D = new _(m);
796
+ const $ = new C(m);
692
797
  export {
693
- v as ContentTypeHeader,
694
- f as ContentTypeValues,
798
+ G as ContentTypeHeader,
799
+ T as ContentTypeValues,
695
800
  m as DEFAULT_FETCHER_NAME,
696
- T as DEFAULT_OPTIONS,
697
- S as FetchExchange,
698
- F as FetchInterceptor,
699
- p as FetchTimeoutError,
700
- q as Fetcher,
701
- N as FetcherInterceptors,
702
- O as FetcherRegistrar,
801
+ g as DEFAULT_OPTIONS,
802
+ f as ExchangeError,
803
+ y as FetchError,
804
+ U as FetchExchange,
805
+ q as FetchInterceptor,
806
+ E as FetchTimeoutError,
807
+ _ as Fetcher,
808
+ a as FetcherError,
809
+ S as FetcherInterceptors,
810
+ L as FetcherRegistrar,
703
811
  c as HttpMethod,
704
- h as InterceptorManager,
705
- _ as NamedFetcher,
706
- R as RequestBodyInterceptor,
707
- g as UrlBuilder,
708
- A as UrlResolveInterceptor,
709
- b as combineURLs,
710
- D as fetcher,
711
- U as fetcherRegistrar,
712
- I as isAbsoluteURL,
713
- d as mergeRecords,
714
- C as mergeRequest,
715
- w as resolveTimeout,
716
- P as timeoutFetch,
717
- a as toSorted
812
+ d as InterceptorManager,
813
+ C as NamedFetcher,
814
+ O as RequestBodyInterceptor,
815
+ R as UrlBuilder,
816
+ N as UrlResolveInterceptor,
817
+ P as combineURLs,
818
+ $ as fetcher,
819
+ v as fetcherRegistrar,
820
+ w as isAbsoluteURL,
821
+ l as mergeRecords,
822
+ j as mergeRequest,
823
+ F as resolveTimeout,
824
+ A as timeoutFetch,
825
+ h as toSorted
718
826
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(n,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(n=typeof globalThis<"u"?globalThis:n||self,a(n.Fetcher={}))})(this,(function(n){"use strict";function a(r){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(r)}function g(r,e){return a(e)?e:e?r.replace(/\/?\/$/,"")+"/"+e.replace(/^\/+/,""):r}class I{constructor(e){this.baseURL=e}build(e,t){const s=t?.path,o=t?.query,u=g(this.baseURL,e);let i=this.interpolateUrl(u,s);if(o){const h=new URLSearchParams(o).toString();h&&(i+="?"+h)}return i}resolveRequestUrl(e){return this.build(e.url,e.urlParams)}interpolateUrl(e,t){return t?e.replace(/{([^}]+)}/g,(s,o)=>{const u=t[o];if(u===void 0)throw new Error(`Missing required path parameter: ${o}`);return String(u)}):e}}function b(r,e){return typeof r<"u"?r:e}class d extends Error{constructor(e){const t=e.method||"GET",s=`Request timeout of ${e.timeout}ms exceeded for ${t} ${e.url}`;super(s),this.name="FetchTimeoutError",this.request=e,Object.setPrototypeOf(this,d.prototype)}}async function F(r){const e=r.url,t=r.timeout,s=r;if(!t)return fetch(e,s);const o=new AbortController,u={...s,signal:o.signal};let i=null;const h=new Promise((B,D)=>{i=setTimeout(()=>{i&&clearTimeout(i);const _=new d(r);o.abort(_),D(_)},t)});try{return await Promise.race([fetch(e,u),h])}finally{i&&clearTimeout(i)}}function l(r,e){return e?r.filter(e).sort((t,s)=>t.order-s.order):[...r].sort((t,s)=>t.order-s.order)}var c=(r=>(r.GET="GET",r.POST="POST",r.PUT="PUT",r.DELETE="DELETE",r.PATCH="PATCH",r.HEAD="HEAD",r.OPTIONS="OPTIONS",r))(c||{});const L="Content-Type";var m=(r=>(r.APPLICATION_JSON="application/json",r.TEXT_EVENT_STREAM="text/event-stream",r))(m||{});class R{constructor(){this.name="RequestBodyInterceptor",this.order=Number.MIN_SAFE_INTEGER+200}intercept(e){const t=e.request;if(t.body===void 0||t.body===null||typeof t.body!="object"||t.body instanceof ArrayBuffer||ArrayBuffer.isView(t.body)||t.body instanceof Blob||t.body instanceof File||t.body instanceof URLSearchParams||t.body instanceof FormData||t.body instanceof ReadableStream)return;const s={...t};s.body=JSON.stringify(t.body),s.headers||(s.headers={});const o=s.headers;o["Content-Type"]||(o["Content-Type"]=m.APPLICATION_JSON),e.request=s}}class w{constructor(){this.name="FetchInterceptor",this.order=Number.MAX_SAFE_INTEGER-100}async intercept(e){e.response=await F(e.request)}}class P{constructor(){this.name="UrlResolveInterceptor",this.order=Number.MIN_SAFE_INTEGER+100}intercept(e){const t=e.request;t.url=e.fetcher.urlBuilder.resolveRequestUrl(t)}}class f{constructor(e=[]){this.sortedInterceptors=[],this.sortedInterceptors=l(e)}get name(){return this.constructor.name}get order(){return Number.MIN_SAFE_INTEGER}use(e){return this.sortedInterceptors.some(t=>t.name===e.name)?!1:(this.sortedInterceptors=l([...this.sortedInterceptors,e]),!0)}eject(e){const t=this.sortedInterceptors;return this.sortedInterceptors=l(t,s=>s.name!==e),t.length!==this.sortedInterceptors.length}clear(){this.sortedInterceptors=[]}async intercept(e){for(const t of this.sortedInterceptors)await t.intercept(e)}}class A{constructor(){this.request=new f([new P,new R,new w]),this.response=new f,this.error=new f}}class N{constructor(e,t,s,o){this.attributes={},this.fetcher=e,this.request=t,this.response=s,this.error=o}hasError(){return!!this.error}hasResponse(){return!!this.response}}function y(r,e){if(!(r===void 0&&e===void 0))return e===void 0?r:r===void 0?e:{...r,...e}}const p={"Content-Type":m.APPLICATION_JSON},E={baseURL:"",headers:p};class S{constructor(e=E){this.headers=p,this.urlBuilder=new I(e.baseURL),this.headers=e.headers??p,this.timeout=e.timeout,this.interceptors=e.interceptors??new A}async fetch(e,t={}){const s=t;s.url=e;const o=await this.request(s);if(!o.response)throw new Error(`Request to ${s.url} failed with no response`);return o.response}async request(e){const t=y(e.headers,this.headers),s={...e,headers:t,timeout:b(e.timeout,this.timeout)},o=new N(this,s);return this.exchange(o)}async exchange(e){try{return await this.interceptors.request.intercept(e),await this.interceptors.response.intercept(e),e}catch(t){if(e.error=t,await this.interceptors.error.intercept(e),e.hasResponse())return e;throw e.error}}async methodFetch(e,t,s={}){return this.fetch(t,{...s,method:e})}async get(e,t={}){return this.methodFetch(c.GET,e,t)}async post(e,t={}){return this.methodFetch(c.POST,e,t)}async put(e,t={}){return this.methodFetch(c.PUT,e,t)}async delete(e,t={}){return this.methodFetch(c.DELETE,e,t)}async patch(e,t={}){return this.methodFetch(c.PATCH,e,t)}async head(e,t={}){return this.methodFetch(c.HEAD,e,t)}async options(e,t={}){return this.methodFetch(c.OPTIONS,e,t)}}const T="default";class q{constructor(){this.registrar=new Map}register(e,t){this.registrar.set(e,t)}unregister(e){return this.registrar.delete(e)}get(e){return this.registrar.get(e)}requiredGet(e){const t=this.get(e);if(!t)throw new Error(`Fetcher ${e} not found`);return t}get default(){return this.requiredGet(T)}set default(e){this.register(T,e)}get fetchers(){return new Map(this.registrar)}}const O=new q;function v(r,e){if(Object.keys(r).length===0)return e;if(Object.keys(e).length===0)return r;const t={path:y(r.urlParams?.path,e.urlParams?.path),query:y(r.urlParams?.query,e.urlParams?.query)},s={...r.headers,...e.headers},o=e.method??r.method,u=e.body??r.body,i=e.timeout??r.timeout,h=e.signal??r.signal;return{...r,...e,method:o,urlParams:t,headers:s,body:u,timeout:i,signal:h}}class U extends S{constructor(e,t=E){super(t),this.name=e,O.register(e,this)}}const C=new U(T);n.ContentTypeHeader=L,n.ContentTypeValues=m,n.DEFAULT_FETCHER_NAME=T,n.DEFAULT_OPTIONS=E,n.FetchExchange=N,n.FetchInterceptor=w,n.FetchTimeoutError=d,n.Fetcher=S,n.FetcherInterceptors=A,n.FetcherRegistrar=q,n.HttpMethod=c,n.InterceptorManager=f,n.NamedFetcher=U,n.RequestBodyInterceptor=R,n.UrlBuilder=I,n.UrlResolveInterceptor=P,n.combineURLs=g,n.fetcher=C,n.fetcherRegistrar=O,n.isAbsoluteURL=a,n.mergeRecords=y,n.mergeRequest=v,n.resolveTimeout=b,n.timeoutFetch=F,n.toSorted=l,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(n,i){typeof exports=="object"&&typeof module<"u"?i(exports):typeof define=="function"&&define.amd?define(["exports"],i):(n=typeof globalThis<"u"?globalThis:n||self,i(n.Fetcher={}))})(this,(function(n){"use strict";class i extends Error{constructor(e,t){const s=e||t?.message||"An error occurred in the fetcher";super(s),this.cause=t,this.name="FetcherError",t?.stack&&(this.stack=t.stack),Object.setPrototypeOf(this,i.prototype)}}class d extends i{constructor(e){const t=e.error?.message||e.response?.statusText||`Request to ${e.request.url} failed during exchange`;super(t,e.error),this.exchange=e,this.name="ExchangeError",Object.setPrototypeOf(this,d.prototype)}}class l extends i{constructor(e,t){const s=t||e.error?.message||`Request to ${e.request.url} failed with no response`;super(s,e.error),this.exchange=e,this.name="FetchError",Object.setPrototypeOf(this,l.prototype)}}class m extends i{constructor(e){const t=e.method||"GET",s=`Request timeout of ${e.timeout}ms exceeded for ${t} ${e.url}`;super(s),this.name="FetchTimeoutError",this.request=e,Object.setPrototypeOf(this,m.prototype)}}function F(r){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(r)}function I(r,e){return F(e)?e:e?r.replace(/\/?\/$/,"")+"/"+e.replace(/^\/+/,""):r}class R{constructor(e){this.baseURL=e}build(e,t){const s=t?.path,o=t?.query,a=I(this.baseURL,e);let c=this.interpolateUrl(a,s);if(o){const h=new URLSearchParams(o).toString();h&&(c+="?"+h)}return c}resolveRequestUrl(e){return this.build(e.url,e.urlParams)}interpolateUrl(e,t){return t?e.replace(/{([^}]+)}/g,(s,o)=>{const a=t[o];if(a===void 0)throw new Error(`Missing required path parameter: ${o}`);return String(a)}):e}}function P(r,e){return typeof r<"u"?r:e}async function w(r){const e=r.url,t=r.timeout,s=r;if(!t)return fetch(e,s);const o=new AbortController,a={...s,signal:o.signal};let c=null;const h=new Promise((G,B)=>{c=setTimeout(()=>{c&&clearTimeout(c);const C=new m(r);o.abort(C),B(C)},t)});try{return await Promise.race([fetch(e,a),h])}finally{c&&clearTimeout(c)}}function f(r,e){return e?r.filter(e).sort((t,s)=>t.order-s.order):[...r].sort((t,s)=>t.order-s.order)}var u=(r=>(r.GET="GET",r.POST="POST",r.PUT="PUT",r.DELETE="DELETE",r.PATCH="PATCH",r.HEAD="HEAD",r.OPTIONS="OPTIONS",r))(u||{});const D="Content-Type";var y=(r=>(r.APPLICATION_JSON="application/json",r.TEXT_EVENT_STREAM="text/event-stream",r))(y||{});class A{constructor(){this.name="RequestBodyInterceptor",this.order=Number.MIN_SAFE_INTEGER+200}intercept(e){const t=e.request;if(t.body===void 0||t.body===null||typeof t.body!="object"||t.body instanceof ArrayBuffer||ArrayBuffer.isView(t.body)||t.body instanceof Blob||t.body instanceof File||t.body instanceof URLSearchParams||t.body instanceof FormData||t.body instanceof ReadableStream)return;const s={...t};s.body=JSON.stringify(t.body),s.headers||(s.headers={});const o=s.headers;o["Content-Type"]||(o["Content-Type"]=y.APPLICATION_JSON),e.request=s}}class O{constructor(){this.name="FetchInterceptor",this.order=Number.MAX_SAFE_INTEGER-100}async intercept(e){e.response=await w(e.request)}}class q{constructor(){this.name="UrlResolveInterceptor",this.order=Number.MIN_SAFE_INTEGER+100}intercept(e){const t=e.request;t.url=e.fetcher.urlBuilder.resolveRequestUrl(t)}}class p{constructor(e=[]){this.sortedInterceptors=[],this.sortedInterceptors=f(e)}get name(){return this.constructor.name}get order(){return Number.MIN_SAFE_INTEGER}use(e){return this.sortedInterceptors.some(t=>t.name===e.name)?!1:(this.sortedInterceptors=f([...this.sortedInterceptors,e]),!0)}eject(e){const t=this.sortedInterceptors;return this.sortedInterceptors=f(t,s=>s.name!==e),t.length!==this.sortedInterceptors.length}clear(){this.sortedInterceptors=[]}async intercept(e){for(const t of this.sortedInterceptors)await t.intercept(e)}}class N{constructor(){this.request=new p([new q,new A,new O]),this.response=new p,this.error=new p}}class S{constructor(e,t,s,o){this.attributes={},this.fetcher=e,this.request=t,this.response=s,this.error=o}hasError(){return!!this.error}hasResponse(){return!!this.response}}function E(r,e){if(!(r===void 0&&e===void 0))return e===void 0?r:r===void 0?e:{...r,...e}}const g={"Content-Type":y.APPLICATION_JSON},b={baseURL:"",headers:g};class U{constructor(e=b){this.headers=g,this.urlBuilder=new R(e.baseURL),this.headers=e.headers??g,this.timeout=e.timeout,this.interceptors=e.interceptors??new N}async fetch(e,t={}){const s=t;s.url=e;const o=await this.request(s);if(!o.response)throw new l(o,`Request to ${s.url} failed with no response`);return o.response}async request(e){const t=E(e.headers,this.headers),s={...e,headers:t,timeout:P(e.timeout,this.timeout)},o=new S(this,s);return this.exchange(o)}async exchange(e){try{return await this.interceptors.request.intercept(e),await this.interceptors.response.intercept(e),e}catch(t){if(e.error=t,await this.interceptors.error.intercept(e),!e.hasError())return e;throw new d(e)}}async methodFetch(e,t,s={}){return this.fetch(t,{...s,method:e})}async get(e,t={}){return this.methodFetch(u.GET,e,t)}async post(e,t={}){return this.methodFetch(u.POST,e,t)}async put(e,t={}){return this.methodFetch(u.PUT,e,t)}async delete(e,t={}){return this.methodFetch(u.DELETE,e,t)}async patch(e,t={}){return this.methodFetch(u.PATCH,e,t)}async head(e,t={}){return this.methodFetch(u.HEAD,e,t)}async options(e,t={}){return this.methodFetch(u.OPTIONS,e,t)}}const T="default";class L{constructor(){this.registrar=new Map}register(e,t){this.registrar.set(e,t)}unregister(e){return this.registrar.delete(e)}get(e){return this.registrar.get(e)}requiredGet(e){const t=this.get(e);if(!t)throw new Error(`Fetcher ${e} not found`);return t}get default(){return this.requiredGet(T)}set default(e){this.register(T,e)}get fetchers(){return new Map(this.registrar)}}const _=new L;function j(r,e){if(Object.keys(r).length===0)return e;if(Object.keys(e).length===0)return r;const t={path:E(r.urlParams?.path,e.urlParams?.path),query:E(r.urlParams?.query,e.urlParams?.query)},s={...r.headers,...e.headers},o=e.method??r.method,a=e.body??r.body,c=e.timeout??r.timeout,h=e.signal??r.signal;return{...r,...e,method:o,urlParams:t,headers:s,body:a,timeout:c,signal:h}}class v extends U{constructor(e,t=b){super(t),this.name=e,_.register(e,this)}}const M=new v(T);n.ContentTypeHeader=D,n.ContentTypeValues=y,n.DEFAULT_FETCHER_NAME=T,n.DEFAULT_OPTIONS=b,n.ExchangeError=d,n.FetchError=l,n.FetchExchange=S,n.FetchInterceptor=O,n.FetchTimeoutError=m,n.Fetcher=U,n.FetcherError=i,n.FetcherInterceptors=N,n.FetcherRegistrar=L,n.HttpMethod=u,n.InterceptorManager=p,n.NamedFetcher=v,n.RequestBodyInterceptor=A,n.UrlBuilder=R,n.UrlResolveInterceptor=q,n.combineURLs=I,n.fetcher=M,n.fetcherRegistrar=_,n.isAbsoluteURL=F,n.mergeRecords=E,n.mergeRequest=j,n.resolveTimeout=P,n.timeoutFetch=w,n.toSorted=f,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
package/dist/timeout.d.ts CHANGED
@@ -25,34 +25,6 @@ export interface TimeoutCapable {
25
25
  * Otherwise, optionsTimeout is returned. If both are undefined, undefined is returned.
26
26
  */
27
27
  export declare function resolveTimeout(requestTimeout?: number, optionsTimeout?: number): number | undefined;
28
- /**
29
- * Exception class thrown when an HTTP request times out.
30
- *
31
- * This error is thrown by the timeoutFetch function when a request exceeds its timeout limit.
32
- *
33
- * @example
34
- * ```typescript
35
- * try {
36
- * const response = await timeoutFetch('https://api.example.com/users', {}, 1000);
37
- * } catch (error) {
38
- * if (error instanceof FetchTimeoutError) {
39
- * console.log(`Request timed out after ${error.timeout}ms`);
40
- * }
41
- * }
42
- * ```
43
- */
44
- export declare class FetchTimeoutError extends Error {
45
- /**
46
- * The request options that timed out.
47
- */
48
- request: FetchRequest;
49
- /**
50
- * Creates a new FetchTimeoutError instance.
51
- *
52
- * @param request - The request options that timed out
53
- */
54
- constructor(request: FetchRequest);
55
- }
56
28
  /**
57
29
  * HTTP request method with timeout control.
58
30
  *
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../src/timeout.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,cAAc,CAAC,EAAE,MAAM,EACvB,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,GAAG,SAAS,CAKpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;;;OAIG;gBACS,OAAO,EAAE,YAAY;CASlC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyC3E"}
1
+ {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../src/timeout.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,cAAc,CAAC,EAAE,MAAM,EACvB,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,GAAG,SAAS,CAKpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyC3E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahoo-wang/fetcher",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Ultra-lightweight (1.9kB) HTTP client with built-in path parameters and Axios-like API",
5
5
  "keywords": [
6
6
  "fetch",