@ahoo-wang/fetcher 0.9.1 → 0.9.5
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 +33 -21
- package/README.zh-CN.md +30 -20
- package/dist/fetchInterceptor.d.ts +27 -8
- package/dist/fetchInterceptor.d.ts.map +1 -1
- package/dist/fetcher.d.ts +8 -25
- package/dist/fetcher.d.ts.map +1 -1
- package/dist/fetcherError.d.ts +62 -0
- package/dist/fetcherError.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +419 -233
- package/dist/index.umd.js +1 -1
- package/dist/interceptor.d.ts +106 -94
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptorManager.d.ts +200 -0
- package/dist/interceptorManager.d.ts.map +1 -0
- package/dist/requestBodyInterceptor.d.ts +24 -6
- package/dist/requestBodyInterceptor.d.ts.map +1 -1
- package/dist/timeout.d.ts +28 -27
- package/dist/timeout.d.ts.map +1 -1
- package/dist/urlBuilder.d.ts +1 -1
- package/dist/urlBuilder.d.ts.map +1 -1
- package/dist/urlResolveInterceptor.d.ts +24 -9
- package/dist/urlResolveInterceptor.d.ts.map +1 -1
- package/dist/validateStatusInterceptor.d.ts +112 -0
- package/dist/validateStatusInterceptor.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.es.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
function
|
|
1
|
+
function b(r) {
|
|
2
2
|
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(r);
|
|
3
3
|
}
|
|
4
|
-
function
|
|
5
|
-
return
|
|
4
|
+
function g(r, t) {
|
|
5
|
+
return b(t) ? t : t ? r.replace(/\/?\/$/, "") + "/" + t.replace(/^\/+/, "") : r;
|
|
6
6
|
}
|
|
7
|
-
class
|
|
7
|
+
class w {
|
|
8
8
|
/**
|
|
9
9
|
* Initializes a new UrlBuilder instance.
|
|
10
10
|
*
|
|
@@ -15,8 +15,8 @@ class g {
|
|
|
15
15
|
* const urlBuilder = new UrlBuilder('https://api.example.com');
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
|
-
constructor(
|
|
19
|
-
this.baseURL =
|
|
18
|
+
constructor(t) {
|
|
19
|
+
this.baseURL = t;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Builds a complete URL, including path parameter replacement and query parameter addition.
|
|
@@ -36,14 +36,14 @@ class g {
|
|
|
36
36
|
* // Result: https://api.example.com/users/123/posts/456?filter=active&limit=10
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
|
-
build(
|
|
40
|
-
const s =
|
|
41
|
-
let
|
|
42
|
-
if (
|
|
43
|
-
const u = new URLSearchParams(
|
|
44
|
-
u && (
|
|
39
|
+
build(t, e) {
|
|
40
|
+
const s = e?.path, o = e?.query, i = g(this.baseURL, t);
|
|
41
|
+
let n = this.interpolateUrl(i, s);
|
|
42
|
+
if (o) {
|
|
43
|
+
const u = new URLSearchParams(o).toString();
|
|
44
|
+
u && (n += "?" + u);
|
|
45
45
|
}
|
|
46
|
-
return
|
|
46
|
+
return n;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
49
|
* Resolves a complete URL from a FetchRequest.
|
|
@@ -54,8 +54,8 @@ class g {
|
|
|
54
54
|
* @param request - The FetchRequest containing URL and URL parameters
|
|
55
55
|
* @returns Complete resolved URL string
|
|
56
56
|
*/
|
|
57
|
-
resolveRequestUrl(
|
|
58
|
-
return this.build(
|
|
57
|
+
resolveRequestUrl(t) {
|
|
58
|
+
return this.build(t.url, t.urlParams);
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* Replaces placeholders in the URL with path parameters.
|
|
@@ -84,60 +84,122 @@ class g {
|
|
|
84
84
|
* }
|
|
85
85
|
* ```
|
|
86
86
|
*/
|
|
87
|
-
interpolateUrl(
|
|
88
|
-
return
|
|
89
|
-
const i =
|
|
87
|
+
interpolateUrl(t, e) {
|
|
88
|
+
return e ? t.replace(/{([^}]+)}/g, (s, o) => {
|
|
89
|
+
const i = e[o];
|
|
90
90
|
if (i === void 0)
|
|
91
|
-
throw new Error(`Missing required path parameter: ${
|
|
91
|
+
throw new Error(`Missing required path parameter: ${o}`);
|
|
92
92
|
return String(i);
|
|
93
|
-
}) :
|
|
93
|
+
}) : t;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
class a extends Error {
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new FetcherError instance.
|
|
99
|
+
*
|
|
100
|
+
* @param errorMsg - Optional error message. If not provided, will use the cause's message or a default message.
|
|
101
|
+
* @param cause - Optional underlying error that caused this error.
|
|
102
|
+
*/
|
|
103
|
+
constructor(t, e) {
|
|
104
|
+
const s = t || e?.message || "An error occurred in the fetcher";
|
|
105
|
+
super(s), this.cause = e, this.name = "FetcherError", e?.stack && (this.stack = e.stack), Object.setPrototypeOf(this, a.prototype);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
class m extends a {
|
|
109
|
+
/**
|
|
110
|
+
* Creates a new FetchError instance.
|
|
111
|
+
*
|
|
112
|
+
* @param exchange - The FetchExchange object containing request/response/error information.
|
|
113
|
+
* @param errorMsg - Optional custom error message. If not provided, will use the exchange's error message.
|
|
114
|
+
*/
|
|
115
|
+
constructor(t, e) {
|
|
116
|
+
const s = e || t.error?.message || `Request to ${t.request.url} failed with no response`;
|
|
117
|
+
super(s, t.error), this.exchange = t, this.name = "FetchError", Object.setPrototypeOf(this, m.prototype);
|
|
118
|
+
}
|
|
98
119
|
}
|
|
99
|
-
class
|
|
120
|
+
class T extends a {
|
|
100
121
|
/**
|
|
101
122
|
* Creates a new FetchTimeoutError instance.
|
|
102
123
|
*
|
|
103
124
|
* @param request - The request options that timed out
|
|
104
125
|
*/
|
|
105
|
-
constructor(
|
|
106
|
-
const
|
|
107
|
-
super(s), this.name = "FetchTimeoutError", this.request =
|
|
126
|
+
constructor(t) {
|
|
127
|
+
const e = t.method || "GET", s = `Request timeout of ${t.timeout}ms exceeded for ${e} ${t.url}`;
|
|
128
|
+
super(s), this.name = "FetchTimeoutError", this.request = t, Object.setPrototypeOf(this, T.prototype);
|
|
108
129
|
}
|
|
109
130
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
131
|
+
function S(r, t) {
|
|
132
|
+
return typeof r < "u" ? r : t;
|
|
133
|
+
}
|
|
134
|
+
async function _(r) {
|
|
135
|
+
const t = r.url, e = r.timeout, s = r;
|
|
136
|
+
if (!e)
|
|
137
|
+
return fetch(t, s);
|
|
138
|
+
const o = new AbortController(), i = {
|
|
115
139
|
...s,
|
|
116
|
-
signal:
|
|
140
|
+
signal: o.signal
|
|
117
141
|
};
|
|
118
|
-
let
|
|
119
|
-
const u = new Promise((
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
},
|
|
142
|
+
let n = null;
|
|
143
|
+
const u = new Promise((z, P) => {
|
|
144
|
+
n = setTimeout(() => {
|
|
145
|
+
n && clearTimeout(n);
|
|
146
|
+
const I = new T(r);
|
|
147
|
+
o.abort(I), P(I);
|
|
148
|
+
}, e);
|
|
125
149
|
});
|
|
126
150
|
try {
|
|
127
|
-
return await Promise.race([fetch(
|
|
151
|
+
return await Promise.race([fetch(t, i), u]);
|
|
128
152
|
} finally {
|
|
129
|
-
|
|
153
|
+
n && clearTimeout(n);
|
|
130
154
|
}
|
|
131
155
|
}
|
|
132
|
-
|
|
133
|
-
|
|
156
|
+
class N {
|
|
157
|
+
constructor(t, e, s, o) {
|
|
158
|
+
this.attributes = {}, this.fetcher = t, this.request = e, this.response = s, this.error = o;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Checks if the exchange has an error.
|
|
162
|
+
*
|
|
163
|
+
* @returns true if an error is present, false otherwise
|
|
164
|
+
*/
|
|
165
|
+
hasError() {
|
|
166
|
+
return !!this.error;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Checks if the exchange has a response.
|
|
170
|
+
*
|
|
171
|
+
* @returns true if a response is present, false otherwise
|
|
172
|
+
*/
|
|
173
|
+
hasResponse() {
|
|
174
|
+
return !!this.response;
|
|
175
|
+
}
|
|
134
176
|
}
|
|
135
177
|
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
|
|
178
|
+
const Q = "Content-Type";
|
|
137
179
|
var f = /* @__PURE__ */ ((r) => (r.APPLICATION_JSON = "application/json", r.TEXT_EVENT_STREAM = "text/event-stream", r))(f || {});
|
|
138
|
-
|
|
180
|
+
function l(r, t) {
|
|
181
|
+
if (!(r === void 0 && t === void 0))
|
|
182
|
+
return t === void 0 ? r : r === void 0 ? t : { ...r, ...t };
|
|
183
|
+
}
|
|
184
|
+
const F = "UrlResolveInterceptor", O = Number.MIN_SAFE_INTEGER + 1e3;
|
|
185
|
+
class q {
|
|
139
186
|
constructor() {
|
|
140
|
-
this.name =
|
|
187
|
+
this.name = F, this.order = O;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Resolves the final URL by combining the base URL, path parameters, and query parameters.
|
|
191
|
+
*
|
|
192
|
+
* @param exchange - The fetch exchange containing the request information
|
|
193
|
+
*/
|
|
194
|
+
intercept(t) {
|
|
195
|
+
const e = t.request;
|
|
196
|
+
e.url = t.fetcher.urlBuilder.resolveRequestUrl(e);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const U = "RequestBodyInterceptor", C = O + 1e3;
|
|
200
|
+
class L {
|
|
201
|
+
constructor() {
|
|
202
|
+
this.name = U, this.order = C;
|
|
141
203
|
}
|
|
142
204
|
/**
|
|
143
205
|
* Attempts to convert request body to a valid fetch API body type.
|
|
@@ -176,20 +238,21 @@ class R {
|
|
|
176
238
|
* // exchange.request.body will be '{"name":"John","age":30}'
|
|
177
239
|
* // exchange.request.headers will include 'Content-Type: application/json'
|
|
178
240
|
*/
|
|
179
|
-
intercept(
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
|
|
241
|
+
intercept(t) {
|
|
242
|
+
const e = t.request;
|
|
243
|
+
if (e.body === void 0 || e.body === null || typeof e.body != "object" || e.body instanceof ArrayBuffer || ArrayBuffer.isView(e.body) || // Includes TypedArray and DataView
|
|
244
|
+
e.body instanceof Blob || e.body instanceof File || e.body instanceof URLSearchParams || e.body instanceof FormData || e.body instanceof ReadableStream)
|
|
183
245
|
return;
|
|
184
|
-
const s = { ...
|
|
185
|
-
s.body = JSON.stringify(
|
|
186
|
-
const
|
|
187
|
-
|
|
246
|
+
const s = { ...e };
|
|
247
|
+
s.body = JSON.stringify(e.body), s.headers || (s.headers = {});
|
|
248
|
+
const o = s.headers;
|
|
249
|
+
o["Content-Type"] || (o["Content-Type"] = f.APPLICATION_JSON), t.request = s;
|
|
188
250
|
}
|
|
189
251
|
}
|
|
190
|
-
|
|
252
|
+
const D = "FetchInterceptor", v = Number.MAX_SAFE_INTEGER - 1e3;
|
|
253
|
+
class M {
|
|
191
254
|
constructor() {
|
|
192
|
-
this.name =
|
|
255
|
+
this.name = D, this.order = v;
|
|
193
256
|
}
|
|
194
257
|
/**
|
|
195
258
|
* Intercept and process HTTP requests.
|
|
@@ -216,27 +279,16 @@ class F {
|
|
|
216
279
|
* await fetchInterceptor.intercept(exchange);
|
|
217
280
|
* console.log(exchange.response); // HTTP response object
|
|
218
281
|
*/
|
|
219
|
-
async intercept(
|
|
220
|
-
|
|
282
|
+
async intercept(t) {
|
|
283
|
+
t.response = await _(t.request);
|
|
221
284
|
}
|
|
222
285
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.name = "UrlResolveInterceptor", this.order = Number.MIN_SAFE_INTEGER + 100;
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Resolves the final URL by combining the base URL, path parameters, and query parameters.
|
|
229
|
-
*
|
|
230
|
-
* @param exchange - The fetch exchange containing the request information
|
|
231
|
-
*/
|
|
232
|
-
intercept(e) {
|
|
233
|
-
const t = e.request;
|
|
234
|
-
t.url = e.fetcher.urlBuilder.resolveRequestUrl(t);
|
|
235
|
-
}
|
|
286
|
+
function h(r, t) {
|
|
287
|
+
return t ? r.filter(t).sort((e, s) => e.order - s.order) : [...r].sort((e, s) => e.order - s.order);
|
|
236
288
|
}
|
|
237
|
-
class
|
|
289
|
+
class d {
|
|
238
290
|
/**
|
|
239
|
-
* Initializes a new
|
|
291
|
+
* Initializes a new InterceptorRegistry instance.
|
|
240
292
|
*
|
|
241
293
|
* @param interceptors - Initial array of interceptors to manage
|
|
242
294
|
*
|
|
@@ -244,11 +296,11 @@ class h {
|
|
|
244
296
|
* The provided interceptors will be sorted by their order property immediately
|
|
245
297
|
* upon construction.
|
|
246
298
|
*/
|
|
247
|
-
constructor(
|
|
248
|
-
this.sortedInterceptors = [], this.sortedInterceptors =
|
|
299
|
+
constructor(t = []) {
|
|
300
|
+
this.sortedInterceptors = [], this.sortedInterceptors = h(t);
|
|
249
301
|
}
|
|
250
302
|
/**
|
|
251
|
-
* Gets the name of this interceptor
|
|
303
|
+
* Gets the name of this interceptor registry.
|
|
252
304
|
*
|
|
253
305
|
* @returns The constructor name of this class
|
|
254
306
|
*/
|
|
@@ -256,15 +308,21 @@ class h {
|
|
|
256
308
|
return this.constructor.name;
|
|
257
309
|
}
|
|
258
310
|
/**
|
|
259
|
-
* Gets the order of this interceptor
|
|
311
|
+
* Gets the order of this interceptor registry.
|
|
260
312
|
*
|
|
261
|
-
* @returns Number.MIN_SAFE_INTEGER, indicating this
|
|
313
|
+
* @returns Number.MIN_SAFE_INTEGER, indicating this registry should execute early
|
|
262
314
|
*/
|
|
263
315
|
get order() {
|
|
264
316
|
return Number.MIN_SAFE_INTEGER;
|
|
265
317
|
}
|
|
266
318
|
/**
|
|
267
|
-
*
|
|
319
|
+
* Returns an array of all interceptors in the registry.
|
|
320
|
+
*/
|
|
321
|
+
get interceptors() {
|
|
322
|
+
return [...this.sortedInterceptors];
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Adds an interceptor to this registry.
|
|
268
326
|
*
|
|
269
327
|
* @param interceptor - The interceptor to add
|
|
270
328
|
* @returns True if the interceptor was added, false if an interceptor with the
|
|
@@ -272,14 +330,14 @@ class h {
|
|
|
272
330
|
*
|
|
273
331
|
* @remarks
|
|
274
332
|
* Interceptors are uniquely identified by their name property. Attempting to add
|
|
275
|
-
* an interceptor with a name that already exists in the
|
|
333
|
+
* an interceptor with a name that already exists in the registry will fail.
|
|
276
334
|
*
|
|
277
335
|
* After adding, interceptors are automatically sorted by their order property.
|
|
278
336
|
*/
|
|
279
|
-
use(
|
|
280
|
-
return this.sortedInterceptors.some((
|
|
337
|
+
use(t) {
|
|
338
|
+
return this.sortedInterceptors.some((e) => e.name === t.name) ? !1 : (this.sortedInterceptors = h([
|
|
281
339
|
...this.sortedInterceptors,
|
|
282
|
-
|
|
340
|
+
t
|
|
283
341
|
]), !0);
|
|
284
342
|
}
|
|
285
343
|
/**
|
|
@@ -289,15 +347,15 @@ class h {
|
|
|
289
347
|
* @returns True if an interceptor was removed, false if no interceptor with the
|
|
290
348
|
* given name was found
|
|
291
349
|
*/
|
|
292
|
-
eject(
|
|
293
|
-
const
|
|
294
|
-
return this.sortedInterceptors =
|
|
295
|
-
|
|
296
|
-
(s) => s.name !==
|
|
297
|
-
),
|
|
350
|
+
eject(t) {
|
|
351
|
+
const e = this.sortedInterceptors;
|
|
352
|
+
return this.sortedInterceptors = h(
|
|
353
|
+
e,
|
|
354
|
+
(s) => s.name !== t
|
|
355
|
+
), e.length !== this.sortedInterceptors.length;
|
|
298
356
|
}
|
|
299
357
|
/**
|
|
300
|
-
* Removes all interceptors from this
|
|
358
|
+
* Removes all interceptors from this registry.
|
|
301
359
|
*/
|
|
302
360
|
clear() {
|
|
303
361
|
this.sortedInterceptors = [];
|
|
@@ -316,52 +374,189 @@ class h {
|
|
|
316
374
|
* If any interceptor throws an error, the execution chain is broken and the error
|
|
317
375
|
* is propagated to the caller.
|
|
318
376
|
*/
|
|
319
|
-
async intercept(
|
|
320
|
-
for (const
|
|
321
|
-
await
|
|
377
|
+
async intercept(t) {
|
|
378
|
+
for (const e of this.sortedInterceptors)
|
|
379
|
+
await e.intercept(t);
|
|
322
380
|
}
|
|
323
381
|
}
|
|
324
|
-
class
|
|
325
|
-
constructor() {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
new F()
|
|
330
|
-
]), this.response = new h(), this.error = new h();
|
|
382
|
+
class R extends a {
|
|
383
|
+
constructor(t) {
|
|
384
|
+
super(
|
|
385
|
+
`Request failed with status code ${t.response?.status} for ${t.request.url}`
|
|
386
|
+
), this.exchange = t, this.name = "HttpStatusValidationError", Object.setPrototypeOf(this, R.prototype);
|
|
331
387
|
}
|
|
332
388
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
389
|
+
const $ = (r) => r >= 200 && r < 300, j = "ValidateStatusInterceptor", B = Number.MAX_SAFE_INTEGER - 1e3;
|
|
390
|
+
class G {
|
|
391
|
+
/**
|
|
392
|
+
* Creates a new ValidateStatusInterceptor instance.
|
|
393
|
+
*
|
|
394
|
+
* @param validateStatus - Function that determines if a status code is valid
|
|
395
|
+
*/
|
|
396
|
+
constructor(t = $) {
|
|
397
|
+
this.validateStatus = t;
|
|
336
398
|
}
|
|
337
399
|
/**
|
|
338
|
-
*
|
|
400
|
+
* Gets the name of this interceptor.
|
|
339
401
|
*
|
|
340
|
-
* @returns
|
|
402
|
+
* @returns The name of this interceptor
|
|
341
403
|
*/
|
|
342
|
-
|
|
343
|
-
return
|
|
404
|
+
get name() {
|
|
405
|
+
return j;
|
|
344
406
|
}
|
|
345
407
|
/**
|
|
346
|
-
*
|
|
408
|
+
* Gets the order of this interceptor.
|
|
347
409
|
*
|
|
348
|
-
* @returns
|
|
410
|
+
* @returns VALIDATE_STATUS_INTERCEPTOR_ORDER, indicating this interceptor should execute early
|
|
349
411
|
*/
|
|
350
|
-
|
|
351
|
-
return
|
|
412
|
+
get order() {
|
|
413
|
+
return B;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Validates the response status code.
|
|
417
|
+
*
|
|
418
|
+
* @param exchange - The exchange containing the response to validate
|
|
419
|
+
* @throws HttpStatusValidationError if the status code is not valid
|
|
420
|
+
*
|
|
421
|
+
* @remarks
|
|
422
|
+
* This method runs at the beginning of the response interceptor chain to ensure
|
|
423
|
+
* status validation happens before any other response processing. Invalid responses
|
|
424
|
+
* are caught and converted to HttpStatusValidationError early in the pipeline,
|
|
425
|
+
* preventing other response handlers from attempting to process them. Valid responses
|
|
426
|
+
* proceed through the rest of the response interceptor chain normally.
|
|
427
|
+
*/
|
|
428
|
+
intercept(t) {
|
|
429
|
+
if (!t.response)
|
|
430
|
+
return;
|
|
431
|
+
const e = t.response.status;
|
|
432
|
+
if (!this.validateStatus(e))
|
|
433
|
+
throw new R(t);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
class y extends a {
|
|
437
|
+
/**
|
|
438
|
+
* Creates a new ExchangeError instance.
|
|
439
|
+
*
|
|
440
|
+
* @param exchange - The FetchExchange object containing request/response/error information.
|
|
441
|
+
*/
|
|
442
|
+
constructor(t) {
|
|
443
|
+
const e = t.error?.message || t.response?.statusText || `Request to ${t.request.url} failed during exchange`;
|
|
444
|
+
super(e, t.error), this.exchange = t, this.name = "ExchangeError", Object.setPrototypeOf(this, y.prototype);
|
|
352
445
|
}
|
|
353
446
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
447
|
+
class V {
|
|
448
|
+
constructor() {
|
|
449
|
+
this.request = new d([
|
|
450
|
+
new q(),
|
|
451
|
+
new L(),
|
|
452
|
+
new M()
|
|
453
|
+
]), this.response = new d([
|
|
454
|
+
new G()
|
|
455
|
+
]), this.error = new d();
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Processes a FetchExchange through the interceptor pipeline.
|
|
459
|
+
*
|
|
460
|
+
* This method is the core of the Fetcher's interceptor system. It executes the three
|
|
461
|
+
* phases of interceptors in sequence:
|
|
462
|
+
* 1. Request interceptors - Process the request before sending
|
|
463
|
+
* 2. Response interceptors - Process the response after receiving
|
|
464
|
+
* 3. Error interceptors - Handle any errors that occurred during the process
|
|
465
|
+
*
|
|
466
|
+
* The interceptor pipeline follows the Chain of Responsibility pattern, where each
|
|
467
|
+
* interceptor can modify the exchange object and decide whether to continue or
|
|
468
|
+
* terminate the chain.
|
|
469
|
+
*
|
|
470
|
+
* @param fetchExchange - The exchange object containing request, response, and error information
|
|
471
|
+
* @returns Promise that resolves to the processed FetchExchange
|
|
472
|
+
* @throws ExchangeError if an unhandled error occurs during processing
|
|
473
|
+
*
|
|
474
|
+
* @remarks
|
|
475
|
+
* The method handles three distinct phases:
|
|
476
|
+
*
|
|
477
|
+
* 1. Request Phase: Executes request interceptors which can modify headers, URL, body, etc.
|
|
478
|
+
* Built-in interceptors handle URL resolution, body serialization, and actual HTTP execution.
|
|
479
|
+
*
|
|
480
|
+
* 2. Response Phase: Executes response interceptors which can transform or validate responses.
|
|
481
|
+
* These interceptors only run if the request phase completed without throwing.
|
|
482
|
+
*
|
|
483
|
+
* 3. Error Phase: Executes error interceptors when any phase throws an error. Error interceptors
|
|
484
|
+
* can handle errors by clearing the error property. If error interceptors clear the error,
|
|
485
|
+
* the exchange is returned successfully.
|
|
486
|
+
*
|
|
487
|
+
* Error Handling:
|
|
488
|
+
* - If any interceptor throws an error, the error phase is triggered
|
|
489
|
+
* - Error interceptors can "fix" errors by clearing the error property on the exchange
|
|
490
|
+
* - If errors remain after error interceptors run, they are wrapped in ExchangeError
|
|
491
|
+
*
|
|
492
|
+
* Order of Execution:
|
|
493
|
+
* 1. Request interceptors (sorted by order property, ascending)
|
|
494
|
+
* 2. Response interceptors (sorted by order property, ascending) - only if no error in request phase
|
|
495
|
+
* 3. Error interceptors (sorted by order property, ascending) - only if an error occurred
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* ```typescript
|
|
499
|
+
* // Create a fetcher with custom interceptors
|
|
500
|
+
* const fetcher = new Fetcher();
|
|
501
|
+
*
|
|
502
|
+
* // Add a request interceptor
|
|
503
|
+
* fetcher.interceptors.request.use({
|
|
504
|
+
* name: 'AuthInterceptor',
|
|
505
|
+
* order: 100,
|
|
506
|
+
* async intercept(exchange: FetchExchange) {
|
|
507
|
+
* exchange.request.headers = {
|
|
508
|
+
* ...exchange.request.headers,
|
|
509
|
+
* 'Authorization': 'Bearer ' + getToken()
|
|
510
|
+
* };
|
|
511
|
+
* }
|
|
512
|
+
* });
|
|
513
|
+
*
|
|
514
|
+
* // Add a response interceptor
|
|
515
|
+
* fetcher.interceptors.response.use({
|
|
516
|
+
* name: 'ResponseLogger',
|
|
517
|
+
* order: 100,
|
|
518
|
+
* async intercept(exchange: FetchExchange) {
|
|
519
|
+
* console.log(`Response status: ${exchange.response?.status}`);
|
|
520
|
+
* }
|
|
521
|
+
* });
|
|
522
|
+
*
|
|
523
|
+
* // Add an error interceptor
|
|
524
|
+
* fetcher.interceptors.error.use({
|
|
525
|
+
* name: 'ErrorLogger',
|
|
526
|
+
* order: 100,
|
|
527
|
+
* async intercept(exchange: FetchExchange) {
|
|
528
|
+
* console.error(`Request to ${exchange.request.url} failed:`, exchange.error);
|
|
529
|
+
* // Clear the error to indicate it's been handled
|
|
530
|
+
* exchange.error = undefined;
|
|
531
|
+
* }
|
|
532
|
+
* });
|
|
533
|
+
*
|
|
534
|
+
* // Create and process an exchange
|
|
535
|
+
* const request: FetchRequest = {
|
|
536
|
+
* url: '/api/users',
|
|
537
|
+
* method: HttpMethod.GET
|
|
538
|
+
* };
|
|
539
|
+
* const exchange = new FetchExchange(fetcher, request);
|
|
540
|
+
* const result = await fetcher.exchange(exchange);
|
|
541
|
+
* ```
|
|
542
|
+
*/
|
|
543
|
+
async exchange(t) {
|
|
544
|
+
try {
|
|
545
|
+
return await this.request.intercept(t), await this.response.intercept(t), t;
|
|
546
|
+
} catch (e) {
|
|
547
|
+
if (t.error = e, await this.error.intercept(t), !t.hasError())
|
|
548
|
+
return t;
|
|
549
|
+
throw new y(t);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
357
552
|
}
|
|
358
|
-
const
|
|
553
|
+
const p = {
|
|
359
554
|
"Content-Type": f.APPLICATION_JSON
|
|
360
|
-
},
|
|
555
|
+
}, A = {
|
|
361
556
|
baseURL: "",
|
|
362
|
-
headers:
|
|
557
|
+
headers: p
|
|
363
558
|
};
|
|
364
|
-
class
|
|
559
|
+
class H {
|
|
365
560
|
/**
|
|
366
561
|
* Initializes a new Fetcher instance with optional configuration.
|
|
367
562
|
*
|
|
@@ -370,8 +565,8 @@ class q {
|
|
|
370
565
|
*
|
|
371
566
|
* @param options - Configuration options for the Fetcher instance
|
|
372
567
|
*/
|
|
373
|
-
constructor(
|
|
374
|
-
this.headers =
|
|
568
|
+
constructor(t = A) {
|
|
569
|
+
this.headers = p, this.urlBuilder = new w(t.baseURL), this.headers = t.headers ?? p, this.timeout = t.timeout, this.interceptors = t.interceptors ?? new V();
|
|
375
570
|
}
|
|
376
571
|
/**
|
|
377
572
|
* Executes an HTTP request with the specified URL and options.
|
|
@@ -382,15 +577,18 @@ class q {
|
|
|
382
577
|
* @param url - The URL path for the request (relative to baseURL if set)
|
|
383
578
|
* @param request - Request configuration including headers, body, parameters, etc.
|
|
384
579
|
* @returns Promise that resolves to the HTTP response
|
|
385
|
-
* @throws
|
|
580
|
+
* @throws FetchError if the request fails and no response is generated
|
|
386
581
|
*/
|
|
387
|
-
async fetch(
|
|
388
|
-
const s =
|
|
389
|
-
s.url =
|
|
390
|
-
const
|
|
391
|
-
if (!
|
|
392
|
-
throw new
|
|
393
|
-
|
|
582
|
+
async fetch(t, e = {}) {
|
|
583
|
+
const s = e;
|
|
584
|
+
s.url = t;
|
|
585
|
+
const o = await this.request(s);
|
|
586
|
+
if (!o.response)
|
|
587
|
+
throw new m(
|
|
588
|
+
o,
|
|
589
|
+
`Request to ${s.url} failed with no response`
|
|
590
|
+
);
|
|
591
|
+
return o.response;
|
|
394
592
|
}
|
|
395
593
|
/**
|
|
396
594
|
* Processes an HTTP request through the Fetcher's internal workflow.
|
|
@@ -403,38 +601,13 @@ class q {
|
|
|
403
601
|
* @returns Promise that resolves to a FetchExchange containing request/response data
|
|
404
602
|
* @throws Error if an unhandled error occurs during request processing
|
|
405
603
|
*/
|
|
406
|
-
async request(
|
|
407
|
-
const
|
|
408
|
-
...
|
|
409
|
-
headers:
|
|
410
|
-
timeout:
|
|
411
|
-
},
|
|
412
|
-
return this.exchange(
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Processes a FetchExchange through the interceptor chain.
|
|
416
|
-
*
|
|
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
|
|
420
|
-
*
|
|
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
|
|
425
|
-
*
|
|
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
|
|
429
|
-
*/
|
|
430
|
-
async exchange(e) {
|
|
431
|
-
try {
|
|
432
|
-
return await this.interceptors.request.intercept(e), await this.interceptors.response.intercept(e), e;
|
|
433
|
-
} catch (t) {
|
|
434
|
-
if (e.error = t, await this.interceptors.error.intercept(e), e.hasResponse())
|
|
435
|
-
return e;
|
|
436
|
-
throw e.error;
|
|
437
|
-
}
|
|
604
|
+
async request(t) {
|
|
605
|
+
const e = l(t.headers, this.headers), s = {
|
|
606
|
+
...t,
|
|
607
|
+
headers: e,
|
|
608
|
+
timeout: S(t.timeout, this.timeout)
|
|
609
|
+
}, o = new N(this, s);
|
|
610
|
+
return this.interceptors.exchange(o);
|
|
438
611
|
}
|
|
439
612
|
/**
|
|
440
613
|
* Internal helper method for making HTTP requests with a specific method.
|
|
@@ -447,10 +620,10 @@ class q {
|
|
|
447
620
|
* @param request - Additional request options
|
|
448
621
|
* @returns Promise that resolves to the HTTP response
|
|
449
622
|
*/
|
|
450
|
-
async methodFetch(
|
|
451
|
-
return this.fetch(
|
|
623
|
+
async methodFetch(t, e, s = {}) {
|
|
624
|
+
return this.fetch(e, {
|
|
452
625
|
...s,
|
|
453
|
-
method:
|
|
626
|
+
method: t
|
|
454
627
|
});
|
|
455
628
|
}
|
|
456
629
|
/**
|
|
@@ -463,8 +636,8 @@ class q {
|
|
|
463
636
|
* @param request - Request options excluding method and body
|
|
464
637
|
* @returns Promise that resolves to the HTTP response
|
|
465
638
|
*/
|
|
466
|
-
async get(
|
|
467
|
-
return this.methodFetch(c.GET,
|
|
639
|
+
async get(t, e = {}) {
|
|
640
|
+
return this.methodFetch(c.GET, t, e);
|
|
468
641
|
}
|
|
469
642
|
/**
|
|
470
643
|
* Makes a POST HTTP request.
|
|
@@ -475,8 +648,8 @@ class q {
|
|
|
475
648
|
* @param request - Request options including body and other parameters
|
|
476
649
|
* @returns Promise that resolves to the HTTP response
|
|
477
650
|
*/
|
|
478
|
-
async post(
|
|
479
|
-
return this.methodFetch(c.POST,
|
|
651
|
+
async post(t, e = {}) {
|
|
652
|
+
return this.methodFetch(c.POST, t, e);
|
|
480
653
|
}
|
|
481
654
|
/**
|
|
482
655
|
* Makes a PUT HTTP request.
|
|
@@ -487,8 +660,8 @@ class q {
|
|
|
487
660
|
* @param request - Request options including body and other parameters
|
|
488
661
|
* @returns Promise that resolves to the HTTP response
|
|
489
662
|
*/
|
|
490
|
-
async put(
|
|
491
|
-
return this.methodFetch(c.PUT,
|
|
663
|
+
async put(t, e = {}) {
|
|
664
|
+
return this.methodFetch(c.PUT, t, e);
|
|
492
665
|
}
|
|
493
666
|
/**
|
|
494
667
|
* Makes a DELETE HTTP request.
|
|
@@ -499,8 +672,8 @@ class q {
|
|
|
499
672
|
* @param request - Request options excluding method and body
|
|
500
673
|
* @returns Promise that resolves to the HTTP response
|
|
501
674
|
*/
|
|
502
|
-
async delete(
|
|
503
|
-
return this.methodFetch(c.DELETE,
|
|
675
|
+
async delete(t, e = {}) {
|
|
676
|
+
return this.methodFetch(c.DELETE, t, e);
|
|
504
677
|
}
|
|
505
678
|
/**
|
|
506
679
|
* Makes a PATCH HTTP request.
|
|
@@ -511,8 +684,8 @@ class q {
|
|
|
511
684
|
* @param request - Request options including body and other parameters
|
|
512
685
|
* @returns Promise that resolves to the HTTP response
|
|
513
686
|
*/
|
|
514
|
-
async patch(
|
|
515
|
-
return this.methodFetch(c.PATCH,
|
|
687
|
+
async patch(t, e = {}) {
|
|
688
|
+
return this.methodFetch(c.PATCH, t, e);
|
|
516
689
|
}
|
|
517
690
|
/**
|
|
518
691
|
* Makes a HEAD HTTP request.
|
|
@@ -524,8 +697,8 @@ class q {
|
|
|
524
697
|
* @param request - Request options excluding method and body
|
|
525
698
|
* @returns Promise that resolves to the HTTP response
|
|
526
699
|
*/
|
|
527
|
-
async head(
|
|
528
|
-
return this.methodFetch(c.HEAD,
|
|
700
|
+
async head(t, e = {}) {
|
|
701
|
+
return this.methodFetch(c.HEAD, t, e);
|
|
529
702
|
}
|
|
530
703
|
/**
|
|
531
704
|
* Makes an OPTIONS HTTP request.
|
|
@@ -537,12 +710,12 @@ class q {
|
|
|
537
710
|
* @param request - Request options excluding method and body
|
|
538
711
|
* @returns Promise that resolves to the HTTP response
|
|
539
712
|
*/
|
|
540
|
-
async options(
|
|
541
|
-
return this.methodFetch(c.OPTIONS,
|
|
713
|
+
async options(t, e = {}) {
|
|
714
|
+
return this.methodFetch(c.OPTIONS, t, e);
|
|
542
715
|
}
|
|
543
716
|
}
|
|
544
|
-
const
|
|
545
|
-
class
|
|
717
|
+
const E = "default";
|
|
718
|
+
class k {
|
|
546
719
|
constructor() {
|
|
547
720
|
this.registrar = /* @__PURE__ */ new Map();
|
|
548
721
|
}
|
|
@@ -555,8 +728,8 @@ class O {
|
|
|
555
728
|
* const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
|
|
556
729
|
* fetcherRegistrar.register('api', fetcher);
|
|
557
730
|
*/
|
|
558
|
-
register(
|
|
559
|
-
this.registrar.set(
|
|
731
|
+
register(t, e) {
|
|
732
|
+
this.registrar.set(t, e);
|
|
560
733
|
}
|
|
561
734
|
/**
|
|
562
735
|
* Unregister a Fetcher instance by name
|
|
@@ -569,8 +742,8 @@ class O {
|
|
|
569
742
|
* console.log('Fetcher unregistered successfully');
|
|
570
743
|
* }
|
|
571
744
|
*/
|
|
572
|
-
unregister(
|
|
573
|
-
return this.registrar.delete(
|
|
745
|
+
unregister(t) {
|
|
746
|
+
return this.registrar.delete(t);
|
|
574
747
|
}
|
|
575
748
|
/**
|
|
576
749
|
* Get a Fetcher instance by name
|
|
@@ -583,8 +756,8 @@ class O {
|
|
|
583
756
|
* // Use the fetcher
|
|
584
757
|
* }
|
|
585
758
|
*/
|
|
586
|
-
get(
|
|
587
|
-
return this.registrar.get(
|
|
759
|
+
get(t) {
|
|
760
|
+
return this.registrar.get(t);
|
|
588
761
|
}
|
|
589
762
|
/**
|
|
590
763
|
* Get a Fetcher instance by name, throwing an error if not found
|
|
@@ -600,11 +773,11 @@ class O {
|
|
|
600
773
|
* console.error('Fetcher not found:', error.message);
|
|
601
774
|
* }
|
|
602
775
|
*/
|
|
603
|
-
requiredGet(
|
|
604
|
-
const
|
|
605
|
-
if (!
|
|
606
|
-
throw new Error(`Fetcher ${
|
|
607
|
-
return
|
|
776
|
+
requiredGet(t) {
|
|
777
|
+
const e = this.get(t);
|
|
778
|
+
if (!e)
|
|
779
|
+
throw new Error(`Fetcher ${t} not found`);
|
|
780
|
+
return e;
|
|
608
781
|
}
|
|
609
782
|
/**
|
|
610
783
|
* Get the default Fetcher instance
|
|
@@ -615,7 +788,7 @@ class O {
|
|
|
615
788
|
* const defaultFetcher = fetcherRegistrar.default;
|
|
616
789
|
*/
|
|
617
790
|
get default() {
|
|
618
|
-
return this.requiredGet(
|
|
791
|
+
return this.requiredGet(E);
|
|
619
792
|
}
|
|
620
793
|
/**
|
|
621
794
|
* Set the default Fetcher instance
|
|
@@ -625,8 +798,8 @@ class O {
|
|
|
625
798
|
* const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
|
|
626
799
|
* fetcherRegistrar.default = fetcher;
|
|
627
800
|
*/
|
|
628
|
-
set default(
|
|
629
|
-
this.register(
|
|
801
|
+
set default(t) {
|
|
802
|
+
this.register(E, t);
|
|
630
803
|
}
|
|
631
804
|
/**
|
|
632
805
|
* Get a copy of all registered fetchers
|
|
@@ -642,31 +815,31 @@ class O {
|
|
|
642
815
|
return new Map(this.registrar);
|
|
643
816
|
}
|
|
644
817
|
}
|
|
645
|
-
const
|
|
646
|
-
function
|
|
818
|
+
const J = new k();
|
|
819
|
+
function Y(r, t) {
|
|
647
820
|
if (Object.keys(r).length === 0)
|
|
648
|
-
return
|
|
649
|
-
if (Object.keys(
|
|
821
|
+
return t;
|
|
822
|
+
if (Object.keys(t).length === 0)
|
|
650
823
|
return r;
|
|
651
|
-
const
|
|
652
|
-
path:
|
|
653
|
-
query:
|
|
824
|
+
const e = {
|
|
825
|
+
path: l(r.urlParams?.path, t.urlParams?.path),
|
|
826
|
+
query: l(r.urlParams?.query, t.urlParams?.query)
|
|
654
827
|
}, s = {
|
|
655
828
|
...r.headers,
|
|
656
|
-
...
|
|
657
|
-
},
|
|
829
|
+
...t.headers
|
|
830
|
+
}, o = t.method ?? r.method, i = t.body ?? r.body, n = t.timeout ?? r.timeout, u = t.signal ?? r.signal;
|
|
658
831
|
return {
|
|
659
832
|
...r,
|
|
660
|
-
...
|
|
661
|
-
method:
|
|
662
|
-
urlParams:
|
|
833
|
+
...t,
|
|
834
|
+
method: o,
|
|
835
|
+
urlParams: e,
|
|
663
836
|
headers: s,
|
|
664
837
|
body: i,
|
|
665
|
-
timeout:
|
|
838
|
+
timeout: n,
|
|
666
839
|
signal: u
|
|
667
840
|
};
|
|
668
841
|
}
|
|
669
|
-
class
|
|
842
|
+
class X extends H {
|
|
670
843
|
/**
|
|
671
844
|
* Create a NamedFetcher instance and automatically register it with the global fetcherRegistrar
|
|
672
845
|
*
|
|
@@ -684,35 +857,48 @@ class _ extends q {
|
|
|
684
857
|
* headers: { 'Authorization': 'Bearer token' }
|
|
685
858
|
* });
|
|
686
859
|
*/
|
|
687
|
-
constructor(
|
|
688
|
-
super(
|
|
860
|
+
constructor(t, e = A) {
|
|
861
|
+
super(e), this.name = t, J.register(t, this);
|
|
689
862
|
}
|
|
690
863
|
}
|
|
691
|
-
const
|
|
864
|
+
const K = new X(E);
|
|
692
865
|
export {
|
|
693
|
-
|
|
866
|
+
Q as ContentTypeHeader,
|
|
694
867
|
f as ContentTypeValues,
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
N as
|
|
702
|
-
|
|
868
|
+
E as DEFAULT_FETCHER_NAME,
|
|
869
|
+
A as DEFAULT_OPTIONS,
|
|
870
|
+
y as ExchangeError,
|
|
871
|
+
D as FETCH_INTERCEPTOR_NAME,
|
|
872
|
+
v as FETCH_INTERCEPTOR_ORDER,
|
|
873
|
+
m as FetchError,
|
|
874
|
+
N as FetchExchange,
|
|
875
|
+
M as FetchInterceptor,
|
|
876
|
+
T as FetchTimeoutError,
|
|
877
|
+
H as Fetcher,
|
|
878
|
+
a as FetcherError,
|
|
879
|
+
k as FetcherRegistrar,
|
|
703
880
|
c as HttpMethod,
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
881
|
+
R as HttpStatusValidationError,
|
|
882
|
+
V as InterceptorManager,
|
|
883
|
+
d as InterceptorRegistry,
|
|
884
|
+
X as NamedFetcher,
|
|
885
|
+
U as REQUEST_BODY_INTERCEPTOR_NAME,
|
|
886
|
+
C as REQUEST_BODY_INTERCEPTOR_ORDER,
|
|
887
|
+
L as RequestBodyInterceptor,
|
|
888
|
+
F as URL_RESOLVE_INTERCEPTOR_NAME,
|
|
889
|
+
O as URL_RESOLVE_INTERCEPTOR_ORDER,
|
|
890
|
+
w as UrlBuilder,
|
|
891
|
+
q as UrlResolveInterceptor,
|
|
892
|
+
j as VALIDATE_STATUS_INTERCEPTOR_NAME,
|
|
893
|
+
B as VALIDATE_STATUS_INTERCEPTOR_ORDER,
|
|
894
|
+
G as ValidateStatusInterceptor,
|
|
895
|
+
g as combineURLs,
|
|
896
|
+
K as fetcher,
|
|
897
|
+
J as fetcherRegistrar,
|
|
898
|
+
b as isAbsoluteURL,
|
|
899
|
+
l as mergeRecords,
|
|
900
|
+
Y as mergeRequest,
|
|
901
|
+
S as resolveTimeout,
|
|
902
|
+
_ as timeoutFetch,
|
|
903
|
+
h as toSorted
|
|
718
904
|
};
|