@ahoo-wang/fetcher-storage 2.8.6 → 2.9.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.
package/dist/index.es.js CHANGED
@@ -1,9 +1,6 @@
1
- function a() {
2
- return typeof window < "u";
3
- }
4
- class n {
1
+ class z {
5
2
  constructor() {
6
- this.store = /* @__PURE__ */ new Map(), this.listeners = /* @__PURE__ */ new Set();
3
+ this.store = /* @__PURE__ */ new Map();
7
4
  }
8
5
  /**
9
6
  * Gets the number of items stored in the storage.
@@ -39,12 +36,7 @@ class n {
39
36
  * @param key - The key of the item to remove
40
37
  */
41
38
  removeItem(e) {
42
- const t = this.getItem(e);
43
- this.store.has(e) && (this.store.delete(e), this.notifyListeners({
44
- key: e,
45
- oldValue: t,
46
- newValue: null
47
- }));
39
+ this.store.delete(e);
48
40
  }
49
41
  /**
50
42
  * Sets an item in the storage.
@@ -52,219 +44,1405 @@ class n {
52
44
  * @param value - The value to set
53
45
  */
54
46
  setItem(e, t) {
55
- const r = this.getItem(e);
56
- this.store.set(e, t), this.notifyListeners({ key: e, oldValue: r, newValue: t });
47
+ this.store.set(e, t);
57
48
  }
49
+ }
50
+ function M() {
51
+ return typeof window < "u";
52
+ }
53
+ const $ = () => M() ? window.localStorage : new z();
54
+ class B {
58
55
  /**
59
- * Adds a listener for storage changes.
60
- * @param listener - The listener function to be called when storage changes
61
- * @returns A function that can be called to remove the listener
56
+ * Serializes a value to a JSON string
57
+ * @param value The value to serialize
58
+ * @returns The JSON string representation of the value
62
59
  */
63
- addListener(e) {
64
- return this.listeners.add(e), () => this.listeners.delete(e);
60
+ serialize(e) {
61
+ return JSON.stringify(e);
65
62
  }
66
63
  /**
67
- * Notifies all listeners of a storage change by creating and dispatching a StorageEvent.
68
- * @param eventInit - The initialization object for the StorageEvent
64
+ * Deserializes a JSON string to a value
65
+ * @param value The JSON string to deserialize
66
+ * @returns The deserialized value
69
67
  */
70
- notifyListeners(e) {
71
- a() && window.location && (e.url = e.url || window.location.href), e.storageArea = this, this.listeners.forEach((t) => {
72
- try {
73
- t(e);
74
- } catch (r) {
75
- console.error("Error in storage change listener:", r);
76
- }
77
- });
68
+ deserialize(e) {
69
+ return JSON.parse(e);
78
70
  }
79
71
  }
80
- class o {
72
+ class V {
81
73
  /**
82
- * Creates a new BrowserListenableStorage instance.
83
- * @param storage - The native Storage object to wrap (e.g., localStorage or sessionStorage)
74
+ * Returns the value as-is without serialization
75
+ * @param value The value to pass through
76
+ * @returns The same value that was passed in
77
+ */
78
+ serialize(e) {
79
+ return e;
80
+ }
81
+ /**
82
+ * Returns the value as-is without deserialization
83
+ * @param value The value to pass through
84
+ * @returns The same value that was passed in
85
+ */
86
+ deserialize(e) {
87
+ return e;
88
+ }
89
+ }
90
+ const qe = new B(), k = new V();
91
+ function G() {
92
+ return k;
93
+ }
94
+ function U(r) {
95
+ return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(r);
96
+ }
97
+ function L(r, e) {
98
+ return U(e) ? e : e ? r.replace(/\/?\/$/, "") + "/" + e.replace(/^\/+/, "") : r;
99
+ }
100
+ function j(r) {
101
+ return r === 1 ? K : D;
102
+ }
103
+ function S(r, e, t) {
104
+ return t ? r.replace(e, (s, n) => {
105
+ const a = t[n];
106
+ if (a === void 0)
107
+ throw new Error(`Missing required path parameter: ${n}`);
108
+ return encodeURIComponent(a);
109
+ }) : r;
110
+ }
111
+ function I(r, e) {
112
+ const t = [];
113
+ let s;
114
+ for (; (s = e.exec(r)) !== null; )
115
+ t.push(s[1]);
116
+ return t;
117
+ }
118
+ const b = class f {
119
+ /**
120
+ * Extracts path parameters from a URL string.
121
+ *
122
+ * @param urlTemplate - The URL string to extract path parameters from
123
+ * @returns An array of path parameter names without the curly braces, or an empty array if no matches found
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const resolver = uriTemplateResolver;
128
+ *
129
+ * // Extract multiple parameters
130
+ * const params = resolver.extractPathParams('/users/{id}/posts/{postId}');
131
+ * // params = ['id', 'postId']
132
+ *
133
+ * // Extract parameters from full URLs
134
+ * const urlParams = resolver.extractPathParams('https://api.example.com/{resource}/{id}');
135
+ * // urlParams = ['resource', 'id']
136
+ *
137
+ * // No parameters
138
+ * const noParams = resolver.extractPathParams('/users/profile');
139
+ * // noParams = []
140
+ * ```
141
+ */
142
+ extractPathParams(e) {
143
+ return I(
144
+ e,
145
+ f.PATH_PARAM_REGEX
146
+ );
147
+ }
148
+ /**
149
+ * Replaces placeholders in the URL with path parameters.
150
+ *
151
+ * @param urlTemplate - Path string containing placeholders, e.g., "http://localhost/users/{id}/posts/{postId}"
152
+ * @param pathParams - Path parameter object used to replace placeholders in the URL
153
+ * @returns Path string with placeholders replaced
154
+ * @throws Error when required path parameters are missing
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * const resolver = uriTemplateResolver;
159
+ *
160
+ * // Replace parameters
161
+ * const url = resolver.resolve('/users/{id}/posts/{postId}', { id: 123, postId: 456 });
162
+ * // url = '/users/123/posts/456'
163
+ *
164
+ * // Handle string parameter values
165
+ * const stringUrl = resolver.resolve('/users/{username}', { username: 'john_doe' });
166
+ * // stringUrl = '/users/john_doe'
167
+ *
168
+ * // URL encode parameter values
169
+ * const encodedUrl = resolver.resolve('/search/{query}', { query: 'hello world' });
170
+ * // encodedUrl = '/search/hello%20world'
171
+ * ```
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * // Missing required parameter throws an error
176
+ * const resolver = uriTemplateResolver;
177
+ * try {
178
+ * resolver.resolve('/users/{id}', { name: 'John' });
179
+ * } catch (error) {
180
+ * console.error(error.message); // "Missing required path parameter: id"
181
+ * }
182
+ * ```
183
+ */
184
+ resolve(e, t) {
185
+ return S(
186
+ e,
187
+ f.PATH_PARAM_REGEX,
188
+ t
189
+ );
190
+ }
191
+ };
192
+ b.PATH_PARAM_REGEX = /{([^}]+)}/g;
193
+ let X = b;
194
+ const D = new X(), H = class y {
195
+ /**
196
+ * Extracts path parameters from an Express-style URL string.
197
+ *
198
+ * @param urlTemplate - The URL string with Express-style parameter placeholders
199
+ * @returns An array of parameter names extracted from the URL template
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const resolver = new expressUrlTemplateResolver;
204
+ *
205
+ * // Extract multiple parameters
206
+ * const params = resolver.extractPathParams('/users/:id/posts/:postId');
207
+ * // params = ['id', 'postId']
208
+ *
209
+ * // No parameters
210
+ * const noParams = resolver.extractPathParams('/users/profile');
211
+ * // noParams = []
212
+ * ```
213
+ */
214
+ extractPathParams(e) {
215
+ return I(
216
+ e,
217
+ y.PATH_PARAM_REGEX
218
+ );
219
+ }
220
+ /**
221
+ * Replaces Express-style placeholders in the URL with path parameters.
222
+ *
223
+ * @param urlTemplate - Path string containing Express-style placeholders
224
+ * @param pathParams - Object containing parameter values to replace placeholders
225
+ * @returns Path string with placeholders replaced
226
+ * @throws Error when required path parameters are missing
227
+ *
228
+ * @example
229
+ * ```typescript
230
+ * const resolver = expressUrlTemplateResolver;
231
+ *
232
+ * // Replace parameters
233
+ * const url = resolver.resolve('/users/:id/posts/:postId', { id: 123, postId: 456 });
234
+ * // url = '/users/123/posts/456'
235
+ *
236
+ * // Handle string parameter values
237
+ * const stringUrl = resolver.resolve('/users/:username', { username: 'john_doe' });
238
+ * // stringUrl = '/users/john_doe'
239
+ * ```
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * // Missing required parameter throws an error
244
+ * const resolver = expressUrlTemplateResolver;
245
+ * try {
246
+ * resolver.resolve('/users/:id', { name: 'John' });
247
+ * } catch (error) {
248
+ * console.error(error.message); // "Missing required path parameter: id"
249
+ * }
250
+ * ```
251
+ */
252
+ resolve(e, t) {
253
+ return S(
254
+ e,
255
+ y.PATH_PARAM_REGEX,
256
+ t
257
+ );
258
+ }
259
+ };
260
+ H.PATH_PARAM_REGEX = /:([^/]+)/g;
261
+ let J = H;
262
+ const K = new J();
263
+ class Q {
264
+ /**
265
+ * Initializes a new UrlBuilder instance.
266
+ *
267
+ * @param baseURL - Base URL that all constructed URLs will be based on
268
+ * @param urlTemplateStyle - Optional style configuration for URL template resolution.
269
+ * Determines how path parameters are parsed and resolved.
270
+ * Defaults to UriTemplate style if not specified.
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * // Create a URL builder with default URI template style
275
+ * const urlBuilder = new UrlBuilder('https://api.example.com');
276
+ *
277
+ * // Create a URL builder with Express-style template resolution
278
+ * const expressUrlBuilder = new UrlBuilder('https://api.example.com', UrlTemplateStyle.Express);
279
+ * ```
280
+ */
281
+ constructor(e, t) {
282
+ this.baseURL = e, this.urlTemplateResolver = j(t);
283
+ }
284
+ /**
285
+ * Builds a complete URL, including path parameter replacement and query parameter addition.
286
+ *
287
+ * @param url - URL path to build (e.g., '/users/{id}/posts')
288
+ * @param params - URL parameters including path and query parameters
289
+ * @returns Complete URL string with base URL, path parameters interpolated, and query string appended
290
+ * @throws Error when required path parameters are missing
291
+ *
292
+ * @example
293
+ * ```typescript
294
+ * const urlBuilder = new UrlBuilder('https://api.example.com');
295
+ * const url = urlBuilder.build('/users/{id}/posts/{postId}', {
296
+ * path: { id: 123, postId: 456 },
297
+ * query: { filter: 'active', limit: 10 }
298
+ * });
299
+ * // Result: https://api.example.com/users/123/posts/456?filter=active&limit=10
300
+ * ```
301
+ */
302
+ build(e, t) {
303
+ const s = t?.path, n = t?.query, a = L(this.baseURL, e);
304
+ let i = this.urlTemplateResolver.resolve(a, s);
305
+ if (n) {
306
+ const c = new URLSearchParams(n).toString();
307
+ c && (i += "?" + c);
308
+ }
309
+ return i;
310
+ }
311
+ /**
312
+ * Resolves a complete URL from a FetchRequest.
313
+ *
314
+ * Used internally by the Fetcher to build the final URL for a request
315
+ * by combining the request URL with its URL parameters using this UrlBuilder.
316
+ *
317
+ * @param request - The FetchRequest containing URL and URL parameters
318
+ * @returns Complete resolved URL string
319
+ */
320
+ resolveRequestUrl(e) {
321
+ return this.build(e.url, e.urlParams);
322
+ }
323
+ }
324
+ class d extends Error {
325
+ /**
326
+ * Creates a new FetcherError instance.
327
+ *
328
+ * @param errorMsg - Optional error message. If not provided, will use the cause's message or a default message.
329
+ * @param cause - Optional underlying error that caused this error.
330
+ */
331
+ constructor(e, t) {
332
+ const s = e || t?.message || "An error occurred in the fetcher";
333
+ super(s), this.cause = t, this.name = "FetcherError", t?.stack && (this.stack = t.stack), Object.setPrototypeOf(this, d.prototype);
334
+ }
335
+ }
336
+ class l extends d {
337
+ /**
338
+ * Creates a new ExchangeError instance.
339
+ *
340
+ * @param exchange - The FetchExchange object containing request/response/error information.
341
+ * @param errorMsg - An optional error message.
342
+ */
343
+ constructor(e, t) {
344
+ const s = t || e.error?.message || e.response?.statusText || `Request to ${e.request.url} failed during exchange`;
345
+ super(s, e.error), this.exchange = e, this.name = "ExchangeError", Object.setPrototypeOf(this, l.prototype);
346
+ }
347
+ }
348
+ class R extends d {
349
+ /**
350
+ * Creates a new FetchTimeoutError instance.
351
+ *
352
+ * @param request - The request options that timed out
84
353
  */
85
354
  constructor(e) {
86
- this.storage = e;
355
+ const t = e.method || "GET", s = `Request timeout of ${e.timeout}ms exceeded for ${t} ${e.url}`;
356
+ super(s), this.name = "FetchTimeoutError", this.request = e, Object.setPrototypeOf(this, R.prototype);
357
+ }
358
+ }
359
+ function Y(r, e) {
360
+ return typeof r < "u" ? r : e;
361
+ }
362
+ async function W(r) {
363
+ const e = r.url, t = r.timeout, s = r;
364
+ if (r.signal)
365
+ return await fetch(e, s);
366
+ if (!t)
367
+ return r.abortController && (s.signal = r.abortController.signal), await fetch(e, s);
368
+ const n = r.abortController ?? new AbortController();
369
+ r.abortController = n, s.signal = n.signal;
370
+ let a = null;
371
+ const i = new Promise((c, F) => {
372
+ a = setTimeout(() => {
373
+ a && clearTimeout(a);
374
+ const A = new R(r);
375
+ n.abort(A), F(A);
376
+ }, t);
377
+ });
378
+ try {
379
+ return await Promise.race([fetch(e, s), i]);
380
+ } finally {
381
+ a && clearTimeout(a);
87
382
  }
383
+ }
384
+ const Z = (r) => r, ee = (r) => r.requiredResponse, T = {
88
385
  /**
89
- * Gets the number of items stored in the storage.
386
+ * Returns the original FetchExchange object
90
387
  */
91
- get length() {
92
- return this.storage.length;
388
+ Exchange: Z,
389
+ /**
390
+ * Extracts the raw Response object
391
+ */
392
+ Response: ee
393
+ };
394
+ function te(r, e) {
395
+ if (e ??= /* @__PURE__ */ new Map(), !r)
396
+ return e;
397
+ if (r instanceof Map) {
398
+ for (const [t, s] of r)
399
+ e.set(t, s);
400
+ return e;
401
+ }
402
+ for (const [t, s] of Object.entries(r))
403
+ e.set(t, s);
404
+ return e;
405
+ }
406
+ class re {
407
+ constructor(e) {
408
+ this.fetcher = e.fetcher, this.request = e.request, this.resultExtractor = e.resultExtractor ?? T.Exchange, this.attributes = te(e.attributes), this._response = e.response, this.error = e.error;
93
409
  }
94
410
  /**
95
- * Adds a listener for storage changes.
96
- * @param listener - The listener function to be called when storage changes
97
- * @returns A function that can be called to remove the listener
411
+ * Ensures that request headers object exists, creating it if necessary.
412
+ *
413
+ * This method checks if the request headers object is present and initializes
414
+ * it as an empty object if it's missing. This guarantees that headers can
415
+ * be safely accessed and modified after calling this method.
416
+ *
417
+ * @returns The request headers object, guaranteed to be non-null
98
418
  */
99
- addListener(e) {
100
- const t = (r) => {
101
- r.storageArea === this.storage && e(r);
102
- };
103
- return window.addEventListener(i, t), () => window.removeEventListener(i, t);
419
+ ensureRequestHeaders() {
420
+ return this.request.headers || (this.request.headers = {}), this.request.headers;
104
421
  }
105
422
  /**
106
- * Clears all items from the storage.
423
+ * Ensures that request URL parameters object exists with all required properties,
424
+ * creating them if necessary.
425
+ *
426
+ * This method checks if the request URL parameters object is present and initializes
427
+ * it with empty path and query objects if it's missing. It also ensures that both
428
+ * path and query sub-objects exist. This guarantees that URL parameters can be
429
+ * safely accessed and modified after calling this method.
430
+ *
431
+ * @returns The request URL parameters object with guaranteed non-null path and query properties
432
+ */
433
+ ensureRequestUrlParams() {
434
+ return this.request.urlParams || (this.request.urlParams = {
435
+ path: {},
436
+ query: {}
437
+ }), this.request.urlParams.path || (this.request.urlParams.path = {}), this.request.urlParams.query || (this.request.urlParams.query = {}), this.request.urlParams;
438
+ }
439
+ /**
440
+ * Checks if the exchange has an error.
441
+ *
442
+ * @returns true if an error is present, false otherwise
443
+ */
444
+ hasError() {
445
+ return !!this.error;
446
+ }
447
+ /**
448
+ * Sets the response object for this exchange.
449
+ * Also invalidates the cached extracted result to ensure data consistency
450
+ * when the response changes.
451
+ *
452
+ * @param response - The Response object to set, or undefined to clear the response
453
+ */
454
+ set response(e) {
455
+ this._response = e, this.cachedExtractedResult = void 0;
456
+ }
457
+ /**
458
+ * Gets the response object for this exchange.
459
+ *
460
+ * @returns The response object if available, undefined otherwise
461
+ */
462
+ get response() {
463
+ return this._response;
464
+ }
465
+ /**
466
+ * Checks if the exchange has a response.
467
+ *
468
+ * @returns true if a response is present, false otherwise
469
+ */
470
+ hasResponse() {
471
+ return !!this.response;
472
+ }
473
+ /**
474
+ * Gets the required response object, throwing an error if no response is available.
475
+ *
476
+ * This getter ensures that a response object is available, and throws an ExchangeError
477
+ * with details about the request if no response was received. This is useful for
478
+ * guaranteeing that downstream code always has a valid Response object to work with.
479
+ *
480
+ * @throws {ExchangeError} If no response is available for the current exchange
481
+ * @returns The Response object for this exchange
482
+ */
483
+ get requiredResponse() {
484
+ if (!this.response)
485
+ throw new l(
486
+ this,
487
+ `Request to ${this.request.url} failed with no response`
488
+ );
489
+ return this.response;
490
+ }
491
+ /**
492
+ * Extracts the result by applying the result extractor to the exchange.
493
+ * The result is cached after the first computation to avoid repeated computations.
494
+ *
495
+ * @returns The extracted result
496
+ */
497
+ async extractResult() {
498
+ return this.cachedExtractedResult !== void 0 ? await this.cachedExtractedResult : (this.cachedExtractedResult = this.resultExtractor(this), await this.cachedExtractedResult);
499
+ }
500
+ }
501
+ var o = /* @__PURE__ */ ((r) => (r.GET = "GET", r.POST = "POST", r.PUT = "PUT", r.DELETE = "DELETE", r.PATCH = "PATCH", r.HEAD = "HEAD", r.OPTIONS = "OPTIONS", r.TRACE = "TRACE", r))(o || {});
502
+ const h = "Content-Type", E = class {
503
+ };
504
+ E.APPLICATION_JSON = "application/json", E.TEXT_EVENT_STREAM = "text/event-stream";
505
+ let O = E;
506
+ const se = "UrlResolveInterceptor", C = Number.MIN_SAFE_INTEGER + 1e3;
507
+ class ne {
508
+ constructor() {
509
+ this.name = se, this.order = C;
510
+ }
511
+ /**
512
+ * Resolves the final URL by combining the base URL, path parameters, and query parameters.
513
+ *
514
+ * @param exchange - The fetch exchange containing the request information
515
+ */
516
+ intercept(e) {
517
+ const t = e.request;
518
+ t.url = e.fetcher.urlBuilder.resolveRequestUrl(t);
519
+ }
520
+ }
521
+ const ae = "RequestBodyInterceptor", oe = C + 1e3;
522
+ class ie {
523
+ constructor() {
524
+ this.name = ae, this.order = oe;
525
+ }
526
+ /**
527
+ * Checks if the provided body is of a supported complex type that doesn't require JSON serialization.
528
+ *
529
+ * @param body - The request body to check
530
+ * @returns True if the body is an ArrayBuffer, TypedArray, DataView or ReadableStream, false otherwise
531
+ */
532
+ isSupportedComplexBodyType(e) {
533
+ return e instanceof ArrayBuffer || ArrayBuffer.isView(e) || e instanceof ReadableStream;
534
+ }
535
+ /**
536
+ * Checks if the provided body is of a type that automatically appends Content-Type header.
537
+ *
538
+ * @param body - The request body to check
539
+ * @returns True if the body is a Blob, File, FormData or URLSearchParams, false otherwise
540
+ */
541
+ isAutoAppendContentType(e) {
542
+ return e instanceof Blob || e instanceof File || e instanceof FormData || e instanceof URLSearchParams;
543
+ }
544
+ /**
545
+ * Attempts to convert request body to a valid fetch API body type.
546
+ *
547
+ * This method follows a specific processing order to handle different types of request bodies:
548
+ * 1. Check if the body is null or undefined and return early if so
549
+ * 2. Check if the body is a non-object type and return early if so
550
+ * 3. Check if the body is a type that automatically appends Content-Type header
551
+ * 4. Check if the body is a supported complex type that doesn't require JSON serialization
552
+ * 5. For plain objects, convert to JSON string and set Content-Type header to application/json
553
+ *
554
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#setting_a_body}
555
+ *
556
+ * Supported types:
557
+ * - a string
558
+ * - ArrayBuffer
559
+ * - TypedArray
560
+ * - DataView
561
+ * - Blob
562
+ * - File
563
+ * - URLSearchParams
564
+ * - FormData
565
+ * - ReadableStream
566
+ *
567
+ * For unsupported object types (like plain objects), they will be automatically
568
+ * converted to JSON strings.
569
+ *
570
+ * @param exchange - The exchange object containing the request to process
571
+ *
572
+ * @example
573
+ * // Plain object body will be converted to JSON
574
+ * const fetcher = new Fetcher();
575
+ * const exchange = new FetchExchange(
576
+ * fetcher,
577
+ * {
578
+ * body: { name: 'John', age: 30 }
579
+ * }
580
+ * );
581
+ * interceptor.intercept(exchange);
582
+ * // exchange.request.body will be '{"name":"John","age":30}'
583
+ * // exchange.request.headers will include 'Content-Type: application/json'
584
+ */
585
+ intercept(e) {
586
+ const t = e.request;
587
+ if (t.body === void 0 || t.body === null || typeof t.body != "object")
588
+ return;
589
+ const s = e.ensureRequestHeaders();
590
+ if (this.isAutoAppendContentType(t.body)) {
591
+ s[h] && delete s[h];
592
+ return;
593
+ }
594
+ this.isSupportedComplexBodyType(t.body) || (e.request.body = JSON.stringify(t.body), s[h] || (s[h] = O.APPLICATION_JSON));
595
+ }
596
+ }
597
+ const ue = "FetchInterceptor", ce = Number.MAX_SAFE_INTEGER - 1e3;
598
+ class he {
599
+ constructor() {
600
+ this.name = ue, this.order = ce;
601
+ }
602
+ /**
603
+ * Intercept and process HTTP requests.
604
+ *
605
+ * Executes the actual HTTP request and applies timeout control. This is the final
606
+ * step in the request processing chain, responsible for calling the timeoutFetch
607
+ * function to send the network request.
608
+ *
609
+ * @param exchange - Exchange object containing request information
610
+ *
611
+ * @throws {FetchTimeoutError} Throws timeout exception when request times out
612
+ *
613
+ * @example
614
+ * // Usually called internally by Fetcher
615
+ * const fetcher = new Fetcher();
616
+ * const exchange = new FetchExchange(
617
+ * fetcher,
618
+ * {
619
+ * url: 'https://api.example.com/users',
620
+ * method: 'GET',
621
+ * timeout: 5000
622
+ * }
623
+ * );
624
+ * await fetchInterceptor.intercept(exchange);
625
+ * console.log(exchange.response); // HTTP response object
626
+ */
627
+ async intercept(e) {
628
+ e.response = await W(e.request);
629
+ }
630
+ }
631
+ const q = 0;
632
+ function x(r, e) {
633
+ return (r.order ?? q) - (e.order ?? q);
634
+ }
635
+ function u(r, e) {
636
+ return e ? r.filter(e).sort(x) : [...r].sort(x);
637
+ }
638
+ let p = class {
639
+ /**
640
+ * Initializes a new InterceptorRegistry instance.
641
+ *
642
+ * @param interceptors - Initial array of interceptors to manage
643
+ *
644
+ * @remarks
645
+ * The provided interceptors will be sorted by their order property immediately
646
+ * upon construction.
647
+ */
648
+ constructor(e = []) {
649
+ this.sortedInterceptors = [], this.sortedInterceptors = u(e);
650
+ }
651
+ /**
652
+ * Gets the name of this interceptor registry.
653
+ *
654
+ * @returns The constructor name of this class
655
+ */
656
+ get name() {
657
+ return this.constructor.name;
658
+ }
659
+ /**
660
+ * Gets the order of this interceptor registry.
661
+ *
662
+ * @returns Number.MIN_SAFE_INTEGER, indicating this registry should execute early
663
+ */
664
+ get order() {
665
+ return Number.MIN_SAFE_INTEGER;
666
+ }
667
+ /**
668
+ * Returns an array of all interceptors in the registry.
669
+ */
670
+ get interceptors() {
671
+ return [...this.sortedInterceptors];
672
+ }
673
+ /**
674
+ * Adds an interceptor to this registry.
675
+ *
676
+ * @param interceptor - The interceptor to add
677
+ * @returns True if the interceptor was added, false if an interceptor with the
678
+ * same name already exists
679
+ *
680
+ * @remarks
681
+ * Interceptors are uniquely identified by their name property. Attempting to add
682
+ * an interceptor with a name that already exists in the registry will fail.
683
+ *
684
+ * After adding, interceptors are automatically sorted by their order property.
685
+ */
686
+ use(e) {
687
+ return this.sortedInterceptors.some((t) => t.name === e.name) ? !1 : (this.sortedInterceptors = u([
688
+ ...this.sortedInterceptors,
689
+ e
690
+ ]), !0);
691
+ }
692
+ /**
693
+ * Removes an interceptor by name.
694
+ *
695
+ * @param name - The name of the interceptor to remove
696
+ * @returns True if an interceptor was removed, false if no interceptor with the
697
+ * given name was found
698
+ */
699
+ eject(e) {
700
+ const t = this.sortedInterceptors;
701
+ return this.sortedInterceptors = u(
702
+ t,
703
+ (s) => s.name !== e
704
+ ), t.length !== this.sortedInterceptors.length;
705
+ }
706
+ /**
707
+ * Removes all interceptors from this registry.
107
708
  */
108
709
  clear() {
109
- this.storage.clear();
710
+ this.sortedInterceptors = [];
110
711
  }
111
712
  /**
112
- * Gets an item from the storage.
113
- * @param key - The key of the item to retrieve
114
- * @returns The value of the item, or null if the item does not exist
713
+ * Executes all managed interceptors on the given exchange object.
714
+ *
715
+ * @param exchange - The exchange object to process
716
+ * @returns A promise that resolves when all interceptors have been executed
717
+ *
718
+ * @remarks
719
+ * Interceptors are executed in order, with each interceptor receiving the result
720
+ * of the previous interceptor. The first interceptor receives the original
721
+ * exchange object.
722
+ *
723
+ * If any interceptor throws an error, the execution chain is broken and the error
724
+ * is propagated to the caller.
115
725
  */
116
- getItem(e) {
117
- return this.storage.getItem(e);
726
+ async intercept(e) {
727
+ for (const t of this.sortedInterceptors)
728
+ await t.intercept(e);
729
+ }
730
+ };
731
+ class P extends l {
732
+ constructor(e) {
733
+ super(
734
+ e,
735
+ `Request failed with status code ${e.response?.status} for ${e.request.url}`
736
+ ), this.name = "HttpStatusValidationError", Object.setPrototypeOf(this, P.prototype);
118
737
  }
738
+ }
739
+ const le = (r) => r >= 200 && r < 300, de = "ValidateStatusInterceptor", pe = Number.MAX_SAFE_INTEGER - 1e3;
740
+ class me {
119
741
  /**
120
- * Gets the key at the specified index.
121
- * @param index - The index of the key to retrieve
122
- * @returns The key at the specified index, or null if the index is out of bounds
742
+ * Creates a new ValidateStatusInterceptor instance.
743
+ *
744
+ * @param validateStatus - Function that determines if a status code is valid
123
745
  */
124
- key(e) {
125
- return this.storage.key(e);
746
+ constructor(e = le) {
747
+ this.validateStatus = e;
126
748
  }
127
749
  /**
128
- * Removes an item from the storage.
129
- * @param key - The key of the item to remove
750
+ * Gets the name of this interceptor.
751
+ *
752
+ * @returns The name of this interceptor
130
753
  */
131
- removeItem(e) {
132
- this.storage.removeItem(e);
754
+ get name() {
755
+ return de;
133
756
  }
134
757
  /**
135
- * Sets an item in the storage.
136
- * @param key - The key of the item to set
137
- * @param value - The value to set
758
+ * Gets the order of this interceptor.
759
+ *
760
+ * @returns VALIDATE_STATUS_INTERCEPTOR_ORDER, indicating this interceptor should execute early
138
761
  */
139
- setItem(e, t) {
140
- this.storage.setItem(e, t);
762
+ get order() {
763
+ return pe;
764
+ }
765
+ /**
766
+ * Validates the response status code.
767
+ *
768
+ * @param exchange - The exchange containing the response to validate
769
+ * @throws HttpStatusValidationError if the status code is not valid
770
+ *
771
+ * @remarks
772
+ * This method runs at the beginning of the response interceptor chain to ensure
773
+ * status validation happens before any other response processing. Invalid responses
774
+ * are caught and converted to HttpStatusValidationError early in the pipeline,
775
+ * preventing other response handlers from attempting to process them. Valid responses
776
+ * proceed through the rest of the response interceptor chain normally.
777
+ */
778
+ intercept(e) {
779
+ if (!e.response)
780
+ return;
781
+ const t = e.response.status;
782
+ if (!this.validateStatus(t))
783
+ throw new P(e);
141
784
  }
142
785
  }
143
- const i = "storage", h = () => a() ? new o(window.localStorage) : new n();
144
- class l {
786
+ class fe {
787
+ constructor() {
788
+ this.request = new p([
789
+ new ne(),
790
+ new ie(),
791
+ new he()
792
+ ]), this.response = new p([
793
+ new me()
794
+ ]), this.error = new p();
795
+ }
145
796
  /**
146
- * Serializes a value to a JSON string
147
- * @param value The value to serialize
148
- * @returns The JSON string representation of the value
797
+ * Processes a FetchExchange through the interceptor pipeline.
798
+ *
799
+ * This method is the core of the Fetcher's interceptor system. It executes the three
800
+ * phases of interceptors in sequence:
801
+ * 1. Request interceptors - Process the request before sending
802
+ * 2. Response interceptors - Process the response after receiving
803
+ * 3. Error interceptors - Handle any errors that occurred during the process
804
+ *
805
+ * The interceptor pipeline follows the Chain of Responsibility pattern, where each
806
+ * interceptor can modify the exchange object and decide whether to continue or
807
+ * terminate the chain.
808
+ *
809
+ * @param fetchExchange - The exchange object containing request, response, and error information
810
+ * @returns Promise that resolves to the processed FetchExchange
811
+ * @throws ExchangeError if an unhandled error occurs during processing
812
+ *
813
+ * @remarks
814
+ * The method handles three distinct phases:
815
+ *
816
+ * 1. Request Phase: Executes request interceptors which can modify headers, URL, body, etc.
817
+ * Built-in interceptors handle URL resolution, body serialization, and actual HTTP execution.
818
+ *
819
+ * 2. Response Phase: Executes response interceptors which can transform or validate responses.
820
+ * These interceptors only run if the request phase completed without throwing.
821
+ *
822
+ * 3. Error Phase: Executes error interceptors when any phase throws an error. Error interceptors
823
+ * can handle errors by clearing the error property. If error interceptors clear the error,
824
+ * the exchange is returned successfully.
825
+ *
826
+ * Error Handling:
827
+ * - If any interceptor throws an error, the error phase is triggered
828
+ * - Error interceptors can "fix" errors by clearing the error property on the exchange
829
+ * - If errors remain after error interceptors run, they are wrapped in ExchangeError
830
+ *
831
+ * Order of Execution:
832
+ * 1. Request interceptors (sorted by order property, ascending)
833
+ * 2. Response interceptors (sorted by order property, ascending) - only if no error in request phase
834
+ * 3. Error interceptors (sorted by order property, ascending) - only if an error occurred
835
+ *
836
+ * @example
837
+ * ```typescript
838
+ * // Create a fetcher with custom interceptors
839
+ * const fetcher = new Fetcher();
840
+ *
841
+ * // Add a request interceptor
842
+ * fetcher.interceptors.request.use({
843
+ * name: 'AuthInterceptor',
844
+ * order: 100,
845
+ * async intercept(exchange: FetchExchange) {
846
+ * exchange.request.headers = {
847
+ * ...exchange.request.headers,
848
+ * 'Authorization': 'Bearer ' + getToken()
849
+ * };
850
+ * }
851
+ * });
852
+ *
853
+ * // Add a response interceptor
854
+ * fetcher.interceptors.response.use({
855
+ * name: 'ResponseLogger',
856
+ * order: 100,
857
+ * async intercept(exchange: FetchExchange) {
858
+ * console.log(`Response status: ${exchange.response?.status}`);
859
+ * }
860
+ * });
861
+ *
862
+ * // Add an error interceptor
863
+ * fetcher.interceptors.error.use({
864
+ * name: 'ErrorLogger',
865
+ * order: 100,
866
+ * async intercept(exchange: FetchExchange) {
867
+ * console.error(`Request to ${exchange.request.url} failed:`, exchange.error);
868
+ * // Clear the error to indicate it's been handled
869
+ * exchange.error = undefined;
870
+ * }
871
+ * });
872
+ *
873
+ * // Create and process an exchange
874
+ * const request: FetchRequest = {
875
+ * url: '/api/users',
876
+ * method: HttpMethod.GET
877
+ * };
878
+ * const exchange = new FetchExchange(fetcher, request);
879
+ * const result = await fetcher.exchange(exchange);
880
+ * ```
149
881
  */
882
+ async exchange(e) {
883
+ try {
884
+ return await this.request.intercept(e), await this.response.intercept(e), e;
885
+ } catch (t) {
886
+ if (e.error = t, await this.error.intercept(e), !e.hasError())
887
+ return e;
888
+ throw new l(e);
889
+ }
890
+ }
891
+ }
892
+ function m(r, e) {
893
+ return e && e.resultExtractor && e.attributes ? e : {
894
+ resultExtractor: e?.resultExtractor ?? r?.resultExtractor ?? _.resultExtractor,
895
+ attributes: e?.attributes ?? r?.attributes
896
+ };
897
+ }
898
+ const g = {
899
+ [h]: O.APPLICATION_JSON
900
+ }, N = {
901
+ baseURL: "",
902
+ headers: g
903
+ }, _ = {
904
+ resultExtractor: T.Exchange
905
+ }, v = {
906
+ resultExtractor: T.Response
907
+ };
908
+ class ye {
150
909
  /**
151
- * Serializes a value to a JSON string
152
- * @param value The value to serialize
153
- * @returns The JSON string representation of the value
910
+ * Initializes a new Fetcher instance with optional configuration.
911
+ *
912
+ * Creates a Fetcher with default settings that can be overridden through the options parameter.
913
+ * If no interceptors are provided, a default set of interceptors will be used.
914
+ *
915
+ * @param options - Configuration options for the Fetcher instance
916
+ * @param options.baseURL - The base URL to prepend to all requests. Defaults to empty string.
917
+ * @param options.headers - Default headers to include in all requests. Defaults to JSON content type.
918
+ * @param options.timeout - Default timeout for requests in milliseconds. No timeout by default.
919
+ * @param options.urlTemplateStyle - Style for URL template parameter interpolation.
920
+ * @param options.interceptors - Interceptor manager for processing requests and responses.
154
921
  */
155
- serialize(e) {
156
- return JSON.stringify(e);
922
+ constructor(e = N) {
923
+ this.headers = g, this.urlBuilder = new Q(e.baseURL, e.urlTemplateStyle), this.headers = e.headers ?? g, this.timeout = e.timeout, this.interceptors = e.interceptors ?? new fe();
157
924
  }
158
925
  /**
159
- * Deserializes a JSON string to a value
160
- * @template V The type of the deserialized value
161
- * @param value The JSON string to deserialize
162
- * @returns The deserialized value
926
+ * Processes an HTTP request through the Fetcher's internal workflow.
927
+ *
928
+ * This method creates a FetchExchange object and passes it through the interceptor chain.
929
+ * It handles header merging, timeout resolution, and result extractor configuration.
930
+ *
931
+ * @param request - The HTTP request configuration to process
932
+ * @param options - Optional request options including result extractor and attributes
933
+ * @returns Promise that resolves to the processed FetchExchange object
163
934
  */
935
+ async exchange(e, t) {
936
+ const s = {
937
+ ...this.headers,
938
+ ...e.headers
939
+ }, n = {
940
+ ...e,
941
+ headers: s,
942
+ timeout: Y(e.timeout, this.timeout)
943
+ }, { resultExtractor: a, attributes: i } = m(
944
+ _,
945
+ t
946
+ ), c = new re({
947
+ fetcher: this,
948
+ request: n,
949
+ resultExtractor: a,
950
+ attributes: i
951
+ });
952
+ return await this.interceptors.exchange(c);
953
+ }
164
954
  /**
165
- * Deserializes a JSON string to a value
166
- * @param value The JSON string to deserialize
167
- * @returns The deserialized value
955
+ * Processes an HTTP request through the Fetcher's internal workflow.
956
+ *
957
+ * This method prepares the request by merging headers and timeout settings,
958
+ * creates a FetchExchange object, and passes it through the exchange method
959
+ * for interceptor processing.
960
+ *
961
+ * @template R - The type of the result to be returned
962
+ * @param request - Complete request configuration object
963
+ * @param options - Request options including result extractor and attributes
964
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
965
+ * Defaults to ExchangeResultExtractor which returns the entire exchange object.
966
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
967
+ * throughout the request lifecycle. These attributes allow passing
968
+ * custom data between different interceptors.
969
+ * @returns Promise that resolves to the extracted result based on resultExtractor
970
+ * @throws Error if an unhandled error occurs during request processing
168
971
  */
169
- deserialize(e) {
170
- return JSON.parse(e);
972
+ async request(e, t) {
973
+ return await (await this.exchange(e, t)).extractResult();
974
+ }
975
+ /**
976
+ * Executes an HTTP request with the specified URL and options.
977
+ *
978
+ * This is the primary method for making HTTP requests. It processes the request
979
+ * through the interceptor chain and returns the resulting Response.
980
+ *
981
+ * @template R - The type of the result to be returned
982
+ * @param url - The URL path for the request (relative to baseURL if set)
983
+ * @param request - Request configuration including headers, body, parameters, etc.
984
+ * @param options - Request options including result extractor and attributes
985
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
986
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
987
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
988
+ * throughout the request lifecycle. These attributes allow passing
989
+ * custom data between different interceptors.
990
+ * @returns Promise that resolves to the HTTP response
991
+ * @throws FetchError if the request fails and no response is generated
992
+ */
993
+ async fetch(e, t = {}, s) {
994
+ const n = {
995
+ ...t,
996
+ url: e
997
+ };
998
+ return await this.request(
999
+ n,
1000
+ m(v, s)
1001
+ );
1002
+ }
1003
+ /**
1004
+ * Internal helper method for making HTTP requests with a specific method.
1005
+ *
1006
+ * This private method is used by the public HTTP method methods (get, post, etc.)
1007
+ * to execute requests with the appropriate HTTP verb.
1008
+ *
1009
+ * @template R - The type of the result to be returned
1010
+ * @param method - The HTTP method to use for the request
1011
+ * @param url - The URL path for the request
1012
+ * @param request - Additional request options
1013
+ * @param options - Request options including result extractor and attributes
1014
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
1015
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
1016
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
1017
+ * throughout the request lifecycle. These attributes allow passing
1018
+ * custom data between different interceptors.
1019
+ * @returns Promise that resolves to the HTTP response
1020
+ */
1021
+ async methodFetch(e, t, s = {}, n) {
1022
+ const a = {
1023
+ ...s,
1024
+ url: t,
1025
+ method: e
1026
+ };
1027
+ return await this.request(
1028
+ a,
1029
+ m(v, n)
1030
+ );
1031
+ }
1032
+ /**
1033
+ * Makes a GET HTTP request.
1034
+ *
1035
+ * Convenience method for making GET requests. The request body is omitted
1036
+ * as GET requests should not contain a body according to HTTP specification.
1037
+ *
1038
+ * @template R - The type of the result to be returned
1039
+ * @param url - The URL path for the request
1040
+ * @param request - Request options excluding method and body
1041
+ * @param options - Request options including result extractor and attributes
1042
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
1043
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
1044
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
1045
+ * throughout the request lifecycle. These attributes allow passing
1046
+ * custom data between different interceptors.
1047
+ * @returns Promise that resolves to the HTTP response
1048
+ */
1049
+ async get(e, t = {}, s) {
1050
+ return await this.methodFetch(o.GET, e, t, s);
1051
+ }
1052
+ /**
1053
+ * Makes a PUT HTTP request.
1054
+ *
1055
+ * Convenience method for making PUT requests, commonly used for updating resources.
1056
+ *
1057
+ * @template R - The type of the result to be returned
1058
+ * @param url - The URL path for the request
1059
+ * @param request - Request options including body and other parameters
1060
+ * @param options - Request options including result extractor and attributes
1061
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
1062
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
1063
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
1064
+ * throughout the request lifecycle. These attributes allow passing
1065
+ * custom data between different interceptors.
1066
+ * @returns Promise that resolves to the HTTP response
1067
+ */
1068
+ async put(e, t = {}, s) {
1069
+ return await this.methodFetch(o.PUT, e, t, s);
1070
+ }
1071
+ /**
1072
+ * Makes a POST HTTP request.
1073
+ *
1074
+ * Convenience method for making POST requests, commonly used for creating resources.
1075
+ *
1076
+ * @template R - The type of the result to be returned
1077
+ * @param url - The URL path for the request
1078
+ * @param request - Request options including body and other parameters
1079
+ * @param options - Request options including result extractor and attributes
1080
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
1081
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
1082
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
1083
+ * throughout the request lifecycle. These attributes allow passing
1084
+ * custom data between different interceptors.
1085
+ * @returns Promise that resolves to the HTTP response
1086
+ */
1087
+ async post(e, t = {}, s) {
1088
+ return await this.methodFetch(o.POST, e, t, s);
1089
+ }
1090
+ /**
1091
+ * Makes a PATCH HTTP request.
1092
+ *
1093
+ * Convenience method for making PATCH requests, commonly used for partial updates.
1094
+ *
1095
+ * @template R - The type of the result to be returned
1096
+ * @param url - The URL path for the request
1097
+ * @param request - Request options including body and other parameters
1098
+ * @param options - Request options including result extractor and attributes
1099
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
1100
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
1101
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
1102
+ * throughout the request lifecycle. These attributes allow passing
1103
+ * custom data between different interceptors.
1104
+ * @returns Promise that resolves to the HTTP response
1105
+ */
1106
+ async patch(e, t = {}, s) {
1107
+ return await this.methodFetch(o.PATCH, e, t, s);
1108
+ }
1109
+ /**
1110
+ * Makes a DELETE HTTP request.
1111
+ *
1112
+ * Convenience method for making DELETE requests, commonly used for deleting resources.
1113
+ *
1114
+ * @template R - The type of the result to be returned
1115
+ * @param url - The URL path for the request
1116
+ * @param request - Request options excluding method and body
1117
+ * @param options - Request options including result extractor and attributes
1118
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
1119
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
1120
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
1121
+ * throughout the request lifecycle. These attributes allow passing
1122
+ * custom data between different interceptors.
1123
+ * @returns Promise that resolves to the HTTP response
1124
+ */
1125
+ async delete(e, t = {}, s) {
1126
+ return await this.methodFetch(o.DELETE, e, t, s);
1127
+ }
1128
+ /**
1129
+ * Makes a HEAD HTTP request.
1130
+ *
1131
+ * Convenience method for making HEAD requests, which retrieve headers only.
1132
+ * The request body is omitted as HEAD requests should not contain a body.
1133
+ *
1134
+ * @template R - The type of the result to be returned
1135
+ * @param url - The URL path for the request
1136
+ * @param request - Request options excluding method and body
1137
+ * @param options - Request options including result extractor and attributes
1138
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
1139
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
1140
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
1141
+ * throughout the request lifecycle. These attributes allow passing
1142
+ * custom data between different interceptors.
1143
+ * @returns Promise that resolves to the HTTP response
1144
+ */
1145
+ async head(e, t = {}, s) {
1146
+ return await this.methodFetch(o.HEAD, e, t, s);
1147
+ }
1148
+ /**
1149
+ * Makes an OPTIONS HTTP request.
1150
+ *
1151
+ * Convenience method for making OPTIONS requests, commonly used for CORS preflight.
1152
+ * The request body is omitted as OPTIONS requests typically don't contain a body.
1153
+ *
1154
+ * @template R - The type of the result to be returned
1155
+ * @param url - The URL path for the request
1156
+ * @param request - Request options excluding method and body
1157
+ * @param options - Request options including result extractor and attributes
1158
+ * @param options.resultExtractor - Function to extract the desired result from the exchange.
1159
+ * Defaults to ResponseResultExtractor which returns the entire exchange object.
1160
+ * @param options.attributes - Optional shared attributes that can be accessed by interceptors
1161
+ * throughout the request lifecycle. These attributes allow passing
1162
+ * custom data between different interceptors.
1163
+ * @returns Promise that resolves to the HTTP response
1164
+ */
1165
+ async options(e, t = {}, s) {
1166
+ return await this.methodFetch(o.OPTIONS, e, t, s);
1167
+ }
1168
+ /**
1169
+ * Sends an HTTP TRACE request to the specified URL and returns the response.
1170
+ *
1171
+ * The TRACE method is used to echo the received request for debugging purposes.
1172
+ * This method automatically sets the HTTP method to TRACE and omits the request body
1173
+ * since TRACE requests must not have a body according to the HTTP specification.
1174
+ *
1175
+ * @param url - The target URL for the TRACE request. Must be a valid absolute or relative URL.
1176
+ * @param request - Request configuration options excluding 'method' and 'body' properties.
1177
+ * Defaults to an empty object. Common properties include headers, cache settings, etc.
1178
+ * @param options - Optional additional request parameters for extended functionality.
1179
+ * May include custom handling logic or metadata for the request pipeline.
1180
+ * @returns A Promise resolving to the response object of type R (defaults to Response).
1181
+ * The response contains status, headers, and body data from the TRACE request.
1182
+ */
1183
+ async trace(e, t = {}, s) {
1184
+ return await this.methodFetch(o.TRACE, e, t, s);
171
1185
  }
172
1186
  }
173
- class c {
1187
+ const w = "default";
1188
+ class Ee {
1189
+ constructor() {
1190
+ this.registrar = /* @__PURE__ */ new Map();
1191
+ }
174
1192
  /**
175
- * Returns the value as-is without serialization
176
- * @param value The value to pass through
177
- * @returns The same value that was passed in
1193
+ * Register a Fetcher instance with a given name
1194
+ *
1195
+ * @param name - The name to register the fetcher under
1196
+ * @param fetcher - The Fetcher instance to register
1197
+ * @example
1198
+ * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
1199
+ * fetcherRegistrar.register('api', fetcher);
178
1200
  */
1201
+ register(e, t) {
1202
+ this.registrar.set(e, t);
1203
+ }
179
1204
  /**
180
- * Returns the value as-is without serialization
181
- * @param value The value to pass through
182
- * @returns The same value that was passed in
1205
+ * Unregister a Fetcher instance by name
1206
+ *
1207
+ * @param name - The name of the fetcher to unregister
1208
+ * @returns boolean - True if the fetcher was successfully unregistered, false otherwise
1209
+ * @example
1210
+ * const success = fetcherRegistrar.unregister('api');
1211
+ * if (success) {
1212
+ * console.log('Fetcher unregistered successfully');
1213
+ * }
183
1214
  */
184
- serialize(e) {
185
- return e;
1215
+ unregister(e) {
1216
+ return this.registrar.delete(e);
186
1217
  }
187
1218
  /**
188
- * Returns the value as-is without deserialization
189
- * @template Deserialized The type of the deserialized value
190
- * @param value The value to pass through
191
- * @returns The same value that was passed in, cast to the target type
1219
+ * Get a Fetcher instance by name
1220
+ *
1221
+ * @param name - The name of the fetcher to retrieve
1222
+ * @returns Fetcher | undefined - The Fetcher instance if found, undefined otherwise
1223
+ * @example
1224
+ * const fetcher = fetcherRegistrar.get('api');
1225
+ * if (fetcher) {
1226
+ * // Use the fetcher
1227
+ * }
192
1228
  */
1229
+ get(e) {
1230
+ return this.registrar.get(e);
1231
+ }
193
1232
  /**
194
- * Returns the value as-is without deserialization
195
- * @param value The value to pass through
196
- * @returns The same value that was passed in
1233
+ * Get a Fetcher instance by name, throwing an error if not found
1234
+ *
1235
+ * @param name - The name of the fetcher to retrieve
1236
+ * @returns Fetcher - The Fetcher instance
1237
+ * @throws Error - If no fetcher is registered with the given name
1238
+ * @example
1239
+ * try {
1240
+ * const fetcher = fetcherRegistrar.requiredGet('api');
1241
+ * // Use the fetcher
1242
+ * } catch (error) {
1243
+ * console.error('Fetcher not found:', error.message);
1244
+ * }
197
1245
  */
198
- deserialize(e) {
199
- return e;
1246
+ requiredGet(e) {
1247
+ const t = this.get(e);
1248
+ if (!t)
1249
+ throw new Error(`Fetcher ${e} not found`);
1250
+ return t;
1251
+ }
1252
+ /**
1253
+ * Get the default Fetcher instance
1254
+ *
1255
+ * @returns Fetcher - The default Fetcher instance
1256
+ * @throws Error - If no default fetcher is registered
1257
+ * @example
1258
+ * const defaultFetcher = fetcherRegistrar.default;
1259
+ */
1260
+ get default() {
1261
+ return this.requiredGet(w);
1262
+ }
1263
+ /**
1264
+ * Set the default Fetcher instance
1265
+ *
1266
+ * @param fetcher - The Fetcher instance to set as default
1267
+ * @example
1268
+ * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
1269
+ * fetcherRegistrar.default = fetcher;
1270
+ */
1271
+ set default(e) {
1272
+ this.register(w, e);
1273
+ }
1274
+ /**
1275
+ * Get a copy of all registered fetchers
1276
+ *
1277
+ * @returns Map<string, Fetcher> - A copy of the internal registrar map
1278
+ * @example
1279
+ * const allFetchers = fetcherRegistrar.fetchers;
1280
+ * for (const [name, fetcher] of allFetchers) {
1281
+ * console.log(`Fetcher ${name}:`, fetcher);
1282
+ * }
1283
+ */
1284
+ get fetchers() {
1285
+ return new Map(this.registrar);
1286
+ }
1287
+ }
1288
+ const ge = new Ee();
1289
+ class we extends ye {
1290
+ /**
1291
+ * Create a NamedFetcher instance and automatically register it with the global fetcherRegistrar
1292
+ *
1293
+ * @param name - The name to register this fetcher under
1294
+ * @param options - Fetcher configuration options (same as Fetcher constructor)
1295
+ *
1296
+ * @example
1297
+ * // Create with default options
1298
+ * const fetcher1 = new NamedFetcher('default');
1299
+ *
1300
+ * // Create with custom options
1301
+ * const fetcher2 = new NamedFetcher('api', {
1302
+ * baseURL: 'https://api.example.com',
1303
+ * timeout: 5000,
1304
+ * headers: { 'Authorization': 'Bearer token' }
1305
+ * });
1306
+ */
1307
+ constructor(e, t = N) {
1308
+ super(t), this.name = e, ge.register(e, this);
200
1309
  }
201
1310
  }
202
- const d = new l(), u = new c();
203
- function g() {
204
- return u;
1311
+ new we(w);
1312
+ class Re {
1313
+ constructor() {
1314
+ this.eventHandlers = [];
1315
+ }
1316
+ /**
1317
+ * Gets a copy of all registered event handlers, sorted by order
1318
+ */
1319
+ get handlers() {
1320
+ return [...this.eventHandlers];
1321
+ }
1322
+ destroy() {
1323
+ this.eventHandlers = [];
1324
+ }
1325
+ async handleEvent(e, t) {
1326
+ try {
1327
+ e.handle(t);
1328
+ } catch (s) {
1329
+ console.warn(`Event handler error for ${e.name}:`, s);
1330
+ }
1331
+ }
205
1332
  }
206
- class w {
1333
+ class Te extends Re {
207
1334
  /**
208
- * Creates a new KeyStorage instance
209
- * @param options Configuration options for the storage
1335
+ * Creates an in-memory typed event bus
1336
+ *
1337
+ * @param type - The event type identifier for this bus
210
1338
  */
211
1339
  constructor(e) {
212
- this.cacheValue = null, this.refreshCacheListener = (t) => {
213
- this.cacheValue = null, t.newValue && this.refreshCache(t.newValue);
214
- }, this.key = e.key, this.serializer = e.serializer ?? g(), this.storage = e.storage ?? h(), this.addListener(this.refreshCacheListener);
1340
+ super(), this.type = e;
215
1341
  }
216
1342
  /**
217
- * Refreshes the cached value by deserializing the provided string
218
- * @param value The serialized value to deserialize and cache
1343
+ * Emits an event to all registered handlers serially
1344
+ *
1345
+ * Handlers are executed in order of their priority. Once-only handlers are removed after execution.
1346
+ * Errors in individual handlers are logged but do not stop other handlers.
1347
+ *
1348
+ * @param event - The event to emit
219
1349
  */
220
- refreshCache(e) {
221
- this.cacheValue = this.serializer.deserialize(e);
1350
+ async emit(e) {
1351
+ const t = [];
1352
+ for (const s of this.eventHandlers)
1353
+ await this.handleEvent(s, e), s.once && t.push(s);
1354
+ t.length > 0 && (this.eventHandlers = u(
1355
+ this.eventHandlers.filter((s) => !t.includes(s))
1356
+ ));
222
1357
  }
223
- addListener(e) {
224
- const t = (r) => {
225
- r.key === this.key && e(r);
226
- };
227
- return this.storage.addListener(t);
1358
+ /**
1359
+ * Removes an event handler by name
1360
+ *
1361
+ * @param name - The name of the handler to remove
1362
+ * @returns true if a handler was removed, false otherwise
1363
+ */
1364
+ off(e) {
1365
+ const t = this.eventHandlers;
1366
+ return t.some((s) => s.name === e) ? (this.eventHandlers = u(t, (s) => s.name !== e), !0) : !1;
1367
+ }
1368
+ /**
1369
+ * Adds an event handler if not already present
1370
+ *
1371
+ * Handlers are sorted by their order property after addition.
1372
+ *
1373
+ * @param handler - The event handler to add
1374
+ * @returns true if the handler was added, false if a handler with the same name already exists
1375
+ */
1376
+ on(e) {
1377
+ const t = this.eventHandlers;
1378
+ return t.some((s) => s.name === e.name) ? !1 : (this.eventHandlers = u([...t, e]), !0);
1379
+ }
1380
+ }
1381
+ class Pe {
1382
+ constructor() {
1383
+ this.namingCounter = 0;
228
1384
  }
229
1385
  /**
230
- * Gets the value associated with the key from storage
231
- * Uses cached value if available, otherwise retrieves from storage and caches it
232
- * @returns The deserialized value or null if no value is found
1386
+ * Generates a unique name by appending an incrementing counter to the prefix
1387
+ * @param prefix - The prefix for the generated name
1388
+ * @returns The generated unique name
233
1389
  */
1390
+ generate(e) {
1391
+ return this.namingCounter++, `${e}_${this.namingCounter}`;
1392
+ }
1393
+ }
1394
+ const Ae = new Pe();
1395
+ class Se {
1396
+ /**
1397
+ * Creates a new KeyStorage instance
1398
+ * @param options Configuration options for the storage
1399
+ */
1400
+ constructor(e) {
1401
+ this.cacheValue = null, this.keyStorageHandler = {
1402
+ name: Ae.generate("KeyStorage"),
1403
+ handle: (t) => {
1404
+ this.cacheValue = t.newValue ?? null;
1405
+ }
1406
+ }, this.key = e.key, this.serializer = e.serializer ?? G(), this.storage = e.storage ?? $(), this.eventBus = e.eventBus ?? new Te(
1407
+ `KeyStorage:${this.key}`
1408
+ ), this.eventBus.on(this.keyStorageHandler);
1409
+ }
1410
+ addListener(e) {
1411
+ return this.eventBus.on(e), () => this.eventBus.off(e.name);
1412
+ }
234
1413
  get() {
235
1414
  if (this.cacheValue)
236
1415
  return this.cacheValue;
237
1416
  const e = this.storage.getItem(this.key);
238
- return e ? (this.refreshCache(e), this.cacheValue) : null;
1417
+ return e ? (this.cacheValue = this.serializer.deserialize(e), this.cacheValue) : null;
239
1418
  }
240
- /**
241
- * Sets the value associated with the key in storage
242
- * Also updates the cached value
243
- * @param value The value to serialize and store
244
- */
245
1419
  set(e) {
246
- const t = this.serializer.serialize(e);
247
- this.storage.setItem(this.key, t), this.cacheValue = e;
1420
+ const t = this.get(), s = this.serializer.serialize(e);
1421
+ this.storage.setItem(this.key, s), this.cacheValue = e, this.eventBus.emit({
1422
+ newValue: e,
1423
+ oldValue: t
1424
+ });
248
1425
  }
249
- /**
250
- * Removes the value associated with the key from storage
251
- * Also clears the cached value
252
- */
253
1426
  remove() {
254
- this.storage.removeItem(this.key), this.cacheValue = null;
1427
+ const e = this.get();
1428
+ this.storage.removeItem(this.key), this.cacheValue = null, this.eventBus.emit({
1429
+ oldValue: e,
1430
+ newValue: null
1431
+ });
1432
+ }
1433
+ destroy() {
1434
+ this.eventBus.off(this.keyStorageHandler.name);
255
1435
  }
256
1436
  }
257
1437
  export {
258
- o as BrowserListenableStorage,
259
- c as IdentitySerializer,
260
- n as InMemoryListenableStorage,
261
- l as JsonSerializer,
262
- w as KeyStorage,
263
- i as STORAGE_EVENT_TYPE,
264
- h as createListenableStorage,
265
- u as identitySerializer,
266
- a as isBrowser,
267
- d as jsonSerializer,
268
- g as typedIdentitySerializer
1438
+ V as IdentitySerializer,
1439
+ z as InMemoryStorage,
1440
+ B as JsonSerializer,
1441
+ Se as KeyStorage,
1442
+ $ as getStorage,
1443
+ k as identitySerializer,
1444
+ M as isBrowser,
1445
+ qe as jsonSerializer,
1446
+ G as typedIdentitySerializer
269
1447
  };
270
1448
  //# sourceMappingURL=index.es.js.map