@ahoo-wang/fetcher-cosec 0.9.6 → 0.9.8

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,870 +1,32 @@
1
- function _(r) {
2
- return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(r);
3
- }
4
- function P(r, t) {
5
- return _(t) ? t : t ? r.replace(/\/?\/$/, "") + "/" + t.replace(/^\/+/, "") : r;
6
- }
7
- class q {
8
- /**
9
- * Initializes a new UrlBuilder instance.
10
- *
11
- * @param baseURL - Base URL that all constructed URLs will be based on
12
- *
13
- * @example
14
- * ```typescript
15
- * const urlBuilder = new UrlBuilder('https://api.example.com');
16
- * ```
17
- */
18
- constructor(t) {
19
- this.baseURL = t;
20
- }
21
- /**
22
- * Builds a complete URL, including path parameter replacement and query parameter addition.
23
- *
24
- * @param url - URL path to build (e.g., '/users/{id}/posts')
25
- * @param params - URL parameters including path and query parameters
26
- * @returns Complete URL string with base URL, path parameters interpolated, and query string appended
27
- * @throws Error when required path parameters are missing
28
- *
29
- * @example
30
- * ```typescript
31
- * const urlBuilder = new UrlBuilder('https://api.example.com');
32
- * const url = urlBuilder.build('/users/{id}/posts/{postId}', {
33
- * path: { id: 123, postId: 456 },
34
- * query: { filter: 'active', limit: 10 }
35
- * });
36
- * // Result: https://api.example.com/users/123/posts/456?filter=active&limit=10
37
- * ```
38
- */
39
- build(t, e) {
40
- const s = e?.path, o = e?.query, i = P(this.baseURL, t);
41
- let n = this.interpolateUrl(i, s);
42
- if (o) {
43
- const h = new URLSearchParams(o).toString();
44
- h && (n += "?" + h);
45
- }
46
- return n;
47
- }
48
- /**
49
- * Resolves a complete URL from a FetchRequest.
50
- *
51
- * Used internally by the Fetcher to build the final URL for a request
52
- * by combining the request URL with its URL parameters using this UrlBuilder.
53
- *
54
- * @param request - The FetchRequest containing URL and URL parameters
55
- * @returns Complete resolved URL string
56
- */
57
- resolveRequestUrl(t) {
58
- return this.build(t.url, t.urlParams);
59
- }
60
- /**
61
- * Replaces placeholders in the URL with path parameters.
62
- *
63
- * @param url - Path string containing placeholders, e.g., "http://localhost/users/{id}/posts/{postId}"
64
- * @param path - Path parameter object used to replace placeholders in the URL
65
- * @returns Path string with placeholders replaced
66
- * @throws Error when required path parameters are missing
67
- *
68
- * @example
69
- * ```typescript
70
- * const urlBuilder = new UrlBuilder('https://api.example.com');
71
- * const result = urlBuilder.interpolateUrl('/users/{id}/posts/{postId}', {
72
- * path: { id: 123, postId: 456 }
73
- * });
74
- * // Result: https://api.example.com/users/123/posts/456
75
- * ```
76
- *
77
- * @example
78
- * ```typescript
79
- * // Missing required parameter throws an error
80
- * try {
81
- * urlBuilder.interpolateUrl('/users/{id}', { name: 'John' });
82
- * } catch (error) {
83
- * console.error(error.message); // "Missing required path parameter: id"
84
- * }
85
- * ```
86
- */
87
- interpolateUrl(t, e) {
88
- return e ? t.replace(/{([^}]+)}/g, (s, o) => {
89
- const i = e[o];
90
- if (i === void 0)
91
- throw new Error(`Missing required path parameter: ${o}`);
92
- return String(i);
93
- }) : t;
94
- }
95
- }
96
- class u 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, u.prototype);
106
- }
107
- }
108
- class g extends u {
109
- /**
110
- * Creates a new FetchTimeoutError instance.
111
- *
112
- * @param request - The request options that timed out
113
- */
114
- constructor(t) {
115
- const e = t.method || "GET", s = `Request timeout of ${t.timeout}ms exceeded for ${e} ${t.url}`;
116
- super(s), this.name = "FetchTimeoutError", this.request = t, Object.setPrototypeOf(this, g.prototype);
117
- }
118
- }
119
- function C(r, t) {
120
- return typeof r < "u" ? r : t;
121
- }
122
- async function b(r) {
123
- const t = r.url, e = r.timeout, s = r;
124
- if (!e)
125
- return fetch(t, s);
126
- const o = new AbortController(), i = {
127
- ...s,
128
- signal: o.signal
129
- };
130
- let n = null;
131
- const h = new Promise((it, N) => {
132
- n = setTimeout(() => {
133
- n && clearTimeout(n);
134
- const y = new g(r);
135
- o.abort(y), N(y);
136
- }, e);
137
- });
138
- try {
139
- return await Promise.race([fetch(t, i), h]);
140
- } finally {
141
- n && clearTimeout(n);
142
- }
143
- }
144
- const v = "UrlResolveInterceptor", T = Number.MIN_SAFE_INTEGER + 1e3;
145
- class D {
146
- constructor() {
147
- this.name = v, this.order = T;
148
- }
149
- /**
150
- * Resolves the final URL by combining the base URL, path parameters, and query parameters.
151
- *
152
- * @param exchange - The fetch exchange containing the request information
153
- */
154
- intercept(t) {
155
- const e = t.request;
156
- e.url = t.fetcher.urlBuilder.resolveRequestUrl(e);
157
- }
158
- }
159
- 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 || {}), f = /* @__PURE__ */ ((r) => (r.APPLICATION_JSON = "application/json", r.TEXT_EVENT_STREAM = "text/event-stream", r))(f || {});
160
- const U = "RequestBodyInterceptor", S = T + 1e3;
161
- class k {
162
- constructor() {
163
- this.name = U, this.order = S;
164
- }
165
- /**
166
- * Attempts to convert request body to a valid fetch API body type.
167
- *
168
- * According to the Fetch API specification, body can be multiple types, but for
169
- * plain objects, they need to be converted to JSON strings.
170
- *
171
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#setting_a_body}
172
- *
173
- * Supported types:
174
- * - a string
175
- * - ArrayBuffer
176
- * - TypedArray
177
- * - DataView
178
- * - Blob
179
- * - File
180
- * - URLSearchParams
181
- * - FormData
182
- * - ReadableStream
183
- *
184
- * For unsupported object types (like plain objects), they will be automatically
185
- * converted to JSON strings.
186
- *
187
- * @param exchange - The exchange object containing the request to process
188
- *
189
- * @example
190
- * // Plain object body will be converted to JSON
191
- * const fetcher = new Fetcher();
192
- * const exchange = new FetchExchange(
193
- * fetcher,
194
- * {
195
- * body: { name: 'John', age: 30 }
196
- * }
197
- * );
198
- * interceptor.intercept(exchange);
199
- * // exchange.request.body will be '{"name":"John","age":30}'
200
- * // exchange.request.headers will include 'Content-Type: application/json'
201
- */
202
- intercept(t) {
203
- const e = t.request;
204
- if (e.body === void 0 || e.body === null || typeof e.body != "object" || e.body instanceof ArrayBuffer || ArrayBuffer.isView(e.body) || // Includes TypedArray and DataView
205
- e.body instanceof Blob || e.body instanceof File || e.body instanceof URLSearchParams || e.body instanceof FormData || e.body instanceof ReadableStream)
206
- return;
207
- const s = { ...e };
208
- s.body = JSON.stringify(e.body), s.headers || (s.headers = {});
209
- const o = s.headers;
210
- o["Content-Type"] || (o["Content-Type"] = f.APPLICATION_JSON), t.request = s;
211
- }
212
- }
213
- const F = "FetchInterceptor", L = Number.MAX_SAFE_INTEGER - 1e3;
214
- class M {
215
- constructor() {
216
- this.name = F, this.order = L;
217
- }
218
- /**
219
- * Intercept and process HTTP requests.
220
- *
221
- * Executes the actual HTTP request and applies timeout control. This is the final
222
- * step in the request processing chain, responsible for calling the timeoutFetch
223
- * function to send the network request.
224
- *
225
- * @param exchange - Exchange object containing request information
226
- *
227
- * @throws {FetchTimeoutError} Throws timeout exception when request times out
228
- *
229
- * @example
230
- * // Usually called internally by Fetcher
231
- * const fetcher = new Fetcher();
232
- * const exchange = new FetchExchange(
233
- * fetcher,
234
- * {
235
- * url: 'https://api.example.com/users',
236
- * method: 'GET',
237
- * timeout: 5000
238
- * }
239
- * );
240
- * await fetchInterceptor.intercept(exchange);
241
- * console.log(exchange.response); // HTTP response object
242
- */
243
- async intercept(t) {
244
- t.response = await b(t.request);
245
- }
246
- }
247
- function l(r, t) {
248
- return t ? r.filter(t).sort((e, s) => e.order - s.order) : [...r].sort((e, s) => e.order - s.order);
249
- }
250
- class p {
251
- /**
252
- * Initializes a new InterceptorRegistry instance.
253
- *
254
- * @param interceptors - Initial array of interceptors to manage
255
- *
256
- * @remarks
257
- * The provided interceptors will be sorted by their order property immediately
258
- * upon construction.
259
- */
260
- constructor(t = []) {
261
- this.sortedInterceptors = [], this.sortedInterceptors = l(t);
262
- }
263
- /**
264
- * Gets the name of this interceptor registry.
265
- *
266
- * @returns The constructor name of this class
267
- */
268
- get name() {
269
- return this.constructor.name;
270
- }
271
- /**
272
- * Gets the order of this interceptor registry.
273
- *
274
- * @returns Number.MIN_SAFE_INTEGER, indicating this registry should execute early
275
- */
276
- get order() {
277
- return Number.MIN_SAFE_INTEGER;
278
- }
279
- /**
280
- * Returns an array of all interceptors in the registry.
281
- */
282
- get interceptors() {
283
- return [...this.sortedInterceptors];
284
- }
285
- /**
286
- * Adds an interceptor to this registry.
287
- *
288
- * @param interceptor - The interceptor to add
289
- * @returns True if the interceptor was added, false if an interceptor with the
290
- * same name already exists
291
- *
292
- * @remarks
293
- * Interceptors are uniquely identified by their name property. Attempting to add
294
- * an interceptor with a name that already exists in the registry will fail.
295
- *
296
- * After adding, interceptors are automatically sorted by their order property.
297
- */
298
- use(t) {
299
- return this.sortedInterceptors.some((e) => e.name === t.name) ? !1 : (this.sortedInterceptors = l([
300
- ...this.sortedInterceptors,
301
- t
302
- ]), !0);
303
- }
304
- /**
305
- * Removes an interceptor by name.
306
- *
307
- * @param name - The name of the interceptor to remove
308
- * @returns True if an interceptor was removed, false if no interceptor with the
309
- * given name was found
310
- */
311
- eject(t) {
312
- const e = this.sortedInterceptors;
313
- return this.sortedInterceptors = l(
314
- e,
315
- (s) => s.name !== t
316
- ), e.length !== this.sortedInterceptors.length;
317
- }
318
- /**
319
- * Removes all interceptors from this registry.
320
- */
321
- clear() {
322
- this.sortedInterceptors = [];
323
- }
324
- /**
325
- * Executes all managed interceptors on the given exchange object.
326
- *
327
- * @param exchange - The exchange object to process
328
- * @returns A promise that resolves when all interceptors have been executed
329
- *
330
- * @remarks
331
- * Interceptors are executed in order, with each interceptor receiving the result
332
- * of the previous interceptor. The first interceptor receives the original
333
- * exchange object.
334
- *
335
- * If any interceptor throws an error, the execution chain is broken and the error
336
- * is propagated to the caller.
337
- */
338
- async intercept(t) {
339
- for (const e of this.sortedInterceptors)
340
- await e.intercept(t);
341
- }
342
- }
343
- class m extends u {
344
- constructor(t) {
345
- super(
346
- `Request failed with status code ${t.response?.status} for ${t.request.url}`
347
- ), this.exchange = t, this.name = "HttpStatusValidationError", Object.setPrototypeOf(this, m.prototype);
348
- }
349
- }
350
- const G = (r) => r >= 200 && r < 300, x = "ValidateStatusInterceptor", K = Number.MAX_SAFE_INTEGER - 1e3;
351
- class H {
352
- /**
353
- * Creates a new ValidateStatusInterceptor instance.
354
- *
355
- * @param validateStatus - Function that determines if a status code is valid
356
- */
357
- constructor(t = G) {
358
- this.validateStatus = t;
359
- }
360
- /**
361
- * Gets the name of this interceptor.
362
- *
363
- * @returns The name of this interceptor
364
- */
365
- get name() {
366
- return x;
367
- }
368
- /**
369
- * Gets the order of this interceptor.
370
- *
371
- * @returns VALIDATE_STATUS_INTERCEPTOR_ORDER, indicating this interceptor should execute early
372
- */
373
- get order() {
374
- return K;
375
- }
376
- /**
377
- * Validates the response status code.
378
- *
379
- * @param exchange - The exchange containing the response to validate
380
- * @throws HttpStatusValidationError if the status code is not valid
381
- *
382
- * @remarks
383
- * This method runs at the beginning of the response interceptor chain to ensure
384
- * status validation happens before any other response processing. Invalid responses
385
- * are caught and converted to HttpStatusValidationError early in the pipeline,
386
- * preventing other response handlers from attempting to process them. Valid responses
387
- * proceed through the rest of the response interceptor chain normally.
388
- */
389
- intercept(t) {
390
- if (!t.response)
391
- return;
392
- const e = t.response.status;
393
- if (!this.validateStatus(e))
394
- throw new m(t);
395
- }
396
- }
397
- class d extends u {
398
- /**
399
- * Creates a new ExchangeError instance.
400
- *
401
- * @param exchange - The FetchExchange object containing request/response/error information.
402
- * @param errorMsg - An optional error message.
403
- */
404
- constructor(t, e) {
405
- const s = e || t.error?.message || t.response?.statusText || `Request to ${t.request.url} failed during exchange`;
406
- super(s, t.error), this.exchange = t, this.name = "ExchangeError", Object.setPrototypeOf(this, d.prototype);
407
- }
408
- }
409
- class $ {
410
- constructor() {
411
- this.request = new p([
412
- new D(),
413
- new k(),
414
- new M()
415
- ]), this.response = new p([
416
- new H()
417
- ]), this.error = new p();
418
- }
419
- /**
420
- * Processes a FetchExchange through the interceptor pipeline.
421
- *
422
- * This method is the core of the Fetcher's interceptor system. It executes the three
423
- * phases of interceptors in sequence:
424
- * 1. Request interceptors - Process the request before sending
425
- * 2. Response interceptors - Process the response after receiving
426
- * 3. Error interceptors - Handle any errors that occurred during the process
427
- *
428
- * The interceptor pipeline follows the Chain of Responsibility pattern, where each
429
- * interceptor can modify the exchange object and decide whether to continue or
430
- * terminate the chain.
431
- *
432
- * @param fetchExchange - The exchange object containing request, response, and error information
433
- * @returns Promise that resolves to the processed FetchExchange
434
- * @throws ExchangeError if an unhandled error occurs during processing
435
- *
436
- * @remarks
437
- * The method handles three distinct phases:
438
- *
439
- * 1. Request Phase: Executes request interceptors which can modify headers, URL, body, etc.
440
- * Built-in interceptors handle URL resolution, body serialization, and actual HTTP execution.
441
- *
442
- * 2. Response Phase: Executes response interceptors which can transform or validate responses.
443
- * These interceptors only run if the request phase completed without throwing.
444
- *
445
- * 3. Error Phase: Executes error interceptors when any phase throws an error. Error interceptors
446
- * can handle errors by clearing the error property. If error interceptors clear the error,
447
- * the exchange is returned successfully.
448
- *
449
- * Error Handling:
450
- * - If any interceptor throws an error, the error phase is triggered
451
- * - Error interceptors can "fix" errors by clearing the error property on the exchange
452
- * - If errors remain after error interceptors run, they are wrapped in ExchangeError
453
- *
454
- * Order of Execution:
455
- * 1. Request interceptors (sorted by order property, ascending)
456
- * 2. Response interceptors (sorted by order property, ascending) - only if no error in request phase
457
- * 3. Error interceptors (sorted by order property, ascending) - only if an error occurred
458
- *
459
- * @example
460
- * ```typescript
461
- * // Create a fetcher with custom interceptors
462
- * const fetcher = new Fetcher();
463
- *
464
- * // Add a request interceptor
465
- * fetcher.interceptors.request.use({
466
- * name: 'AuthInterceptor',
467
- * order: 100,
468
- * async intercept(exchange: FetchExchange) {
469
- * exchange.request.headers = {
470
- * ...exchange.request.headers,
471
- * 'Authorization': 'Bearer ' + getToken()
472
- * };
473
- * }
474
- * });
475
- *
476
- * // Add a response interceptor
477
- * fetcher.interceptors.response.use({
478
- * name: 'ResponseLogger',
479
- * order: 100,
480
- * async intercept(exchange: FetchExchange) {
481
- * console.log(`Response status: ${exchange.response?.status}`);
482
- * }
483
- * });
484
- *
485
- * // Add an error interceptor
486
- * fetcher.interceptors.error.use({
487
- * name: 'ErrorLogger',
488
- * order: 100,
489
- * async intercept(exchange: FetchExchange) {
490
- * console.error(`Request to ${exchange.request.url} failed:`, exchange.error);
491
- * // Clear the error to indicate it's been handled
492
- * exchange.error = undefined;
493
- * }
494
- * });
495
- *
496
- * // Create and process an exchange
497
- * const request: FetchRequest = {
498
- * url: '/api/users',
499
- * method: HttpMethod.GET
500
- * };
501
- * const exchange = new FetchExchange(fetcher, request);
502
- * const result = await fetcher.exchange(exchange);
503
- * ```
504
- */
505
- async exchange(t) {
506
- try {
507
- return await this.request.intercept(t), await this.response.intercept(t), t;
508
- } catch (e) {
509
- if (t.error = e, await this.error.intercept(t), !t.hasError())
510
- return t;
511
- throw new d(t);
512
- }
513
- }
514
- }
515
- class V {
516
- constructor(t, e, s, o) {
517
- this.attributes = {}, this.fetcher = t, this.request = e, this.response = s, this.error = o;
518
- }
519
- /**
520
- * Checks if the exchange has an error.
521
- *
522
- * @returns true if an error is present, false otherwise
523
- */
524
- hasError() {
525
- return !!this.error;
526
- }
527
- /**
528
- * Checks if the exchange has a response.
529
- *
530
- * @returns true if a response is present, false otherwise
531
- */
532
- hasResponse() {
533
- return !!this.response;
534
- }
535
- /**
536
- * Gets the required response object, throwing an error if no response is available.
537
- *
538
- * This getter ensures that a response object is available, and throws an ExchangeError
539
- * with details about the request if no response was received. This is useful for
540
- * guaranteeing that downstream code always has a valid Response object to work with.
541
- *
542
- * @throws {ExchangeError} If no response is available for the current exchange
543
- * @returns The Response object for this exchange
544
- */
545
- get requiredResponse() {
546
- if (!this.response)
547
- throw new d(
548
- this,
549
- `Request to ${this.request.url} failed with no response`
550
- );
551
- return this.response;
552
- }
553
- }
554
- function j(r, t) {
555
- if (!(r === void 0 && t === void 0))
556
- return t === void 0 ? r : r === void 0 ? t : { ...r, ...t };
557
- }
558
- const E = {
559
- "Content-Type": f.APPLICATION_JSON
560
- }, R = {
561
- baseURL: "",
562
- headers: E
563
- };
564
- class z {
565
- /**
566
- * Initializes a new Fetcher instance with optional configuration.
567
- *
568
- * Creates a Fetcher with default settings that can be overridden through the options parameter.
569
- * If no interceptors are provided, a default set of interceptors will be used.
570
- *
571
- * @param options - Configuration options for the Fetcher instance
572
- */
573
- constructor(t = R) {
574
- this.headers = E, this.urlBuilder = new q(t.baseURL), this.headers = t.headers ?? E, this.timeout = t.timeout, this.interceptors = t.interceptors ?? new $();
575
- }
576
- /**
577
- * Executes an HTTP request with the specified URL and options.
578
- *
579
- * This is the primary method for making HTTP requests. It processes the request
580
- * through the interceptor chain and returns the resulting Response.
581
- *
582
- * @param url - The URL path for the request (relative to baseURL if set)
583
- * @param request - Request configuration including headers, body, parameters, etc.
584
- * @returns Promise that resolves to the HTTP response
585
- * @throws FetchError if the request fails and no response is generated
586
- */
587
- async fetch(t, e = {}) {
588
- const s = e;
589
- return s.url = t, (await this.request(s)).requiredResponse;
590
- }
591
- /**
592
- * Processes an HTTP request through the Fetcher's internal workflow.
593
- *
594
- * This method prepares the request by merging headers and timeout settings,
595
- * creates a FetchExchange object, and passes it through the exchange method
596
- * for interceptor processing.
597
- *
598
- * @param request - Complete request configuration object
599
- * @returns Promise that resolves to a FetchExchange containing request/response data
600
- * @throws Error if an unhandled error occurs during request processing
601
- */
602
- async request(t) {
603
- const e = j(t.headers, this.headers), s = {
604
- ...t,
605
- headers: e,
606
- timeout: C(t.timeout, this.timeout)
607
- }, o = new V(this, s);
608
- return this.interceptors.exchange(o);
609
- }
610
- /**
611
- * Internal helper method for making HTTP requests with a specific method.
612
- *
613
- * This private method is used by the public HTTP method methods (get, post, etc.)
614
- * to execute requests with the appropriate HTTP verb.
615
- *
616
- * @param method - The HTTP method to use for the request
617
- * @param url - The URL path for the request
618
- * @param request - Additional request options
619
- * @returns Promise that resolves to the HTTP response
620
- */
621
- async methodFetch(t, e, s = {}) {
622
- return this.fetch(e, {
623
- ...s,
624
- method: t
625
- });
626
- }
627
- /**
628
- * Makes a GET HTTP request.
629
- *
630
- * Convenience method for making GET requests. The request body is omitted
631
- * as GET requests should not contain a body according to HTTP specification.
632
- *
633
- * @param url - The URL path for the request
634
- * @param request - Request options excluding method and body
635
- * @returns Promise that resolves to the HTTP response
636
- */
637
- async get(t, e = {}) {
638
- return this.methodFetch(c.GET, t, e);
639
- }
640
- /**
641
- * Makes a POST HTTP request.
642
- *
643
- * Convenience method for making POST requests, commonly used for creating resources.
644
- *
645
- * @param url - The URL path for the request
646
- * @param request - Request options including body and other parameters
647
- * @returns Promise that resolves to the HTTP response
648
- */
649
- async post(t, e = {}) {
650
- return this.methodFetch(c.POST, t, e);
651
- }
652
- /**
653
- * Makes a PUT HTTP request.
654
- *
655
- * Convenience method for making PUT requests, commonly used for updating resources.
656
- *
657
- * @param url - The URL path for the request
658
- * @param request - Request options including body and other parameters
659
- * @returns Promise that resolves to the HTTP response
660
- */
661
- async put(t, e = {}) {
662
- return this.methodFetch(c.PUT, t, e);
663
- }
664
- /**
665
- * Makes a DELETE HTTP request.
666
- *
667
- * Convenience method for making DELETE requests, commonly used for deleting resources.
668
- *
669
- * @param url - The URL path for the request
670
- * @param request - Request options excluding method and body
671
- * @returns Promise that resolves to the HTTP response
672
- */
673
- async delete(t, e = {}) {
674
- return this.methodFetch(c.DELETE, t, e);
675
- }
676
- /**
677
- * Makes a PATCH HTTP request.
678
- *
679
- * Convenience method for making PATCH requests, commonly used for partial updates.
680
- *
681
- * @param url - The URL path for the request
682
- * @param request - Request options including body and other parameters
683
- * @returns Promise that resolves to the HTTP response
684
- */
685
- async patch(t, e = {}) {
686
- return this.methodFetch(c.PATCH, t, e);
687
- }
688
- /**
689
- * Makes a HEAD HTTP request.
690
- *
691
- * Convenience method for making HEAD requests, which retrieve headers only.
692
- * The request body is omitted as HEAD requests should not contain a body.
693
- *
694
- * @param url - The URL path for the request
695
- * @param request - Request options excluding method and body
696
- * @returns Promise that resolves to the HTTP response
697
- */
698
- async head(t, e = {}) {
699
- return this.methodFetch(c.HEAD, t, e);
700
- }
701
- /**
702
- * Makes an OPTIONS HTTP request.
703
- *
704
- * Convenience method for making OPTIONS requests, commonly used for CORS preflight.
705
- * The request body is omitted as OPTIONS requests typically don't contain a body.
706
- *
707
- * @param url - The URL path for the request
708
- * @param request - Request options excluding method and body
709
- * @returns Promise that resolves to the HTTP response
710
- */
711
- async options(t, e = {}) {
712
- return this.methodFetch(c.OPTIONS, t, e);
713
- }
714
- }
715
- const I = "default";
716
- class B {
717
- constructor() {
718
- this.registrar = /* @__PURE__ */ new Map();
719
- }
720
- /**
721
- * Register a Fetcher instance with a given name
722
- *
723
- * @param name - The name to register the fetcher under
724
- * @param fetcher - The Fetcher instance to register
725
- * @example
726
- * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
727
- * fetcherRegistrar.register('api', fetcher);
728
- */
729
- register(t, e) {
730
- this.registrar.set(t, e);
731
- }
732
- /**
733
- * Unregister a Fetcher instance by name
734
- *
735
- * @param name - The name of the fetcher to unregister
736
- * @returns boolean - True if the fetcher was successfully unregistered, false otherwise
737
- * @example
738
- * const success = fetcherRegistrar.unregister('api');
739
- * if (success) {
740
- * console.log('Fetcher unregistered successfully');
741
- * }
742
- */
743
- unregister(t) {
744
- return this.registrar.delete(t);
745
- }
746
- /**
747
- * Get a Fetcher instance by name
748
- *
749
- * @param name - The name of the fetcher to retrieve
750
- * @returns Fetcher | undefined - The Fetcher instance if found, undefined otherwise
751
- * @example
752
- * const fetcher = fetcherRegistrar.get('api');
753
- * if (fetcher) {
754
- * // Use the fetcher
755
- * }
756
- */
757
- get(t) {
758
- return this.registrar.get(t);
759
- }
760
- /**
761
- * Get a Fetcher instance by name, throwing an error if not found
762
- *
763
- * @param name - The name of the fetcher to retrieve
764
- * @returns Fetcher - The Fetcher instance
765
- * @throws Error - If no fetcher is registered with the given name
766
- * @example
767
- * try {
768
- * const fetcher = fetcherRegistrar.requiredGet('api');
769
- * // Use the fetcher
770
- * } catch (error) {
771
- * console.error('Fetcher not found:', error.message);
772
- * }
773
- */
774
- requiredGet(t) {
775
- const e = this.get(t);
776
- if (!e)
777
- throw new Error(`Fetcher ${t} not found`);
778
- return e;
779
- }
780
- /**
781
- * Get the default Fetcher instance
782
- *
783
- * @returns Fetcher - The default Fetcher instance
784
- * @throws Error - If no default fetcher is registered
785
- * @example
786
- * const defaultFetcher = fetcherRegistrar.default;
787
- */
788
- get default() {
789
- return this.requiredGet(I);
790
- }
791
- /**
792
- * Set the default Fetcher instance
793
- *
794
- * @param fetcher - The Fetcher instance to set as default
795
- * @example
796
- * const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
797
- * fetcherRegistrar.default = fetcher;
798
- */
799
- set default(t) {
800
- this.register(I, t);
801
- }
802
- /**
803
- * Get a copy of all registered fetchers
804
- *
805
- * @returns Map<string, Fetcher> - A copy of the internal registrar map
806
- * @example
807
- * const allFetchers = fetcherRegistrar.fetchers;
808
- * for (const [name, fetcher] of allFetchers) {
809
- * console.log(`Fetcher ${name}:`, fetcher);
810
- * }
811
- */
812
- get fetchers() {
813
- return new Map(this.registrar);
814
- }
815
- }
816
- const J = new B();
817
- class X extends z {
818
- /**
819
- * Create a NamedFetcher instance and automatically register it with the global fetcherRegistrar
820
- *
821
- * @param name - The name to register this fetcher under
822
- * @param options - Fetcher configuration options (same as Fetcher constructor)
823
- *
824
- * @example
825
- * // Create with default options
826
- * const fetcher1 = new NamedFetcher('default');
827
- *
828
- * // Create with custom options
829
- * const fetcher2 = new NamedFetcher('api', {
830
- * baseURL: 'https://api.example.com',
831
- * timeout: 5000,
832
- * headers: { 'Authorization': 'Bearer token' }
833
- * });
834
- */
835
- constructor(t, e = R) {
836
- super(e), this.name = t, J.register(t, this);
837
- }
838
- }
839
- new X(I);
840
- var a = /* @__PURE__ */ ((r) => (r.DEVICE_ID = "CoSec-Device-Id", r.APP_ID = "CoSec-App-Id", r.AUTHORIZATION = "Authorization", r.REQUEST_ID = "CoSec-Request-Id", r))(a || {}), w = /* @__PURE__ */ ((r) => (r[r.UNAUTHORIZED = 401] = "UNAUTHORIZED", r))(w || {});
841
- const ct = {
1
+ import { REQUEST_BODY_INTERCEPTOR_ORDER as h } from "@ahoo-wang/fetcher";
2
+ var o = /* @__PURE__ */ ((r) => (r.DEVICE_ID = "CoSec-Device-Id", r.APP_ID = "CoSec-App-Id", r.AUTHORIZATION = "Authorization", r.REQUEST_ID = "CoSec-Request-Id", r))(o || {}), c = /* @__PURE__ */ ((r) => (r[r.UNAUTHORIZED = 401] = "UNAUTHORIZED", r))(c || {});
3
+ const C = {
842
4
  ALLOW: { authorized: !0, reason: "Allow" },
843
5
  EXPLICIT_DENY: { authorized: !1, reason: "Explicit Deny" },
844
6
  IMPLICIT_DENY: { authorized: !1, reason: "Implicit Deny" },
845
7
  TOKEN_EXPIRED: { authorized: !1, reason: "Token Expired" },
846
8
  TOO_MANY_REQUESTS: { authorized: !1, reason: "Too Many Requests" }
847
- }, Q = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
848
- let Y = (r = 21) => {
849
- let t = "", e = crypto.getRandomValues(new Uint8Array(r |= 0));
9
+ }, u = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
10
+ let d = (r = 21) => {
11
+ let e = "", t = crypto.getRandomValues(new Uint8Array(r |= 0));
850
12
  for (; r--; )
851
- t += Q[e[r] & 63];
852
- return t;
13
+ e += u[t[r] & 63];
14
+ return e;
853
15
  };
854
- class Z {
16
+ class l {
855
17
  /**
856
18
  * Generate a unique request ID.
857
19
  *
858
20
  * @returns A unique request ID
859
21
  */
860
22
  generateId() {
861
- return Y();
23
+ return d();
862
24
  }
863
25
  }
864
- const O = new Z(), W = "CoSecRequestInterceptor", tt = S + 1e3;
865
- class at {
866
- constructor(t) {
867
- this.name = W, this.order = tt, this.options = t;
26
+ const I = new l(), R = "CoSecRequestInterceptor", g = h + 1e3;
27
+ class N {
28
+ constructor(e) {
29
+ this.name = R, this.order = g, this.options = e;
868
30
  }
869
31
  /**
870
32
  * Intercept requests to add CoSec authentication headers.
@@ -886,44 +48,44 @@ class at {
886
48
  * This execution order prevents authentication headers from being overwritten by
887
49
  * subsequent request processing interceptors.
888
50
  */
889
- intercept(t) {
890
- const e = O.generateId(), s = this.options.deviceIdStorage.getOrCreate(), o = this.options.tokenStorage.get(), i = {
891
- ...t.request,
51
+ intercept(e) {
52
+ const t = I.generateId(), n = this.options.deviceIdStorage.getOrCreate(), s = this.options.tokenStorage.get(), a = {
53
+ ...e.request,
892
54
  headers: {
893
- ...t.request.headers
55
+ ...e.request.headers
894
56
  }
895
- }, n = i.headers;
896
- n[a.APP_ID] = this.options.appId, n[a.DEVICE_ID] = s, n[a.REQUEST_ID] = e, o && (n[a.AUTHORIZATION] = `Bearer ${o.accessToken}`), t.request = i;
57
+ }, i = a.headers;
58
+ i[o.APP_ID] = this.options.appId, i[o.DEVICE_ID] = n, i[o.REQUEST_ID] = t, s && (i[o.AUTHORIZATION] = `Bearer ${s.accessToken}`), e.request = a;
897
59
  }
898
60
  }
899
- const et = "CoSecResponseInterceptor", rt = Number.MIN_SAFE_INTEGER + 1e3;
900
- class ut {
61
+ const S = "CoSecResponseInterceptor", T = Number.MIN_SAFE_INTEGER + 1e3;
62
+ class y {
901
63
  /**
902
64
  * Creates a new CoSecResponseInterceptor instance.
903
65
  * @param options - The CoSec configuration options including token storage and refresher
904
66
  */
905
- constructor(t) {
906
- this.name = et, this.order = rt, this.options = t;
67
+ constructor(e) {
68
+ this.name = S, this.order = T, this.options = e;
907
69
  }
908
70
  /**
909
71
  * Intercepts the response and handles unauthorized responses by refreshing tokens.
910
72
  * @param exchange - The fetch exchange containing request and response information
911
73
  */
912
- async intercept(t) {
913
- const e = t.response;
914
- if (!e || e.status !== w.UNAUTHORIZED)
74
+ async intercept(e) {
75
+ const t = e.response;
76
+ if (!t || t.status !== c.UNAUTHORIZED)
915
77
  return;
916
- const s = this.options.tokenStorage.get();
917
- if (s)
78
+ const n = this.options.tokenStorage.get();
79
+ if (n)
918
80
  try {
919
- const o = await this.options.tokenRefresher.refresh(s);
920
- this.options.tokenStorage.set(o), await t.fetcher.request(t.request);
921
- } catch (o) {
922
- throw this.options.tokenStorage.clear(), o;
81
+ const s = await this.options.tokenRefresher.refresh(n);
82
+ this.options.tokenStorage.set(s), await e.fetcher.request(e.request);
83
+ } catch (s) {
84
+ throw this.options.tokenStorage.clear(), s;
923
85
  }
924
86
  }
925
87
  }
926
- class st {
88
+ class _ {
927
89
  constructor() {
928
90
  this.store = /* @__PURE__ */ new Map();
929
91
  }
@@ -933,27 +95,27 @@ class st {
933
95
  clear() {
934
96
  this.store.clear();
935
97
  }
936
- getItem(t) {
937
- const e = this.store.get(t);
938
- return e !== void 0 ? e : null;
98
+ getItem(e) {
99
+ const t = this.store.get(e);
100
+ return t !== void 0 ? t : null;
939
101
  }
940
- key(t) {
941
- return Array.from(this.store.keys())[t] || null;
102
+ key(e) {
103
+ return Array.from(this.store.keys())[e] || null;
942
104
  }
943
- removeItem(t) {
944
- this.store.has(t) && this.store.delete(t);
105
+ removeItem(e) {
106
+ this.store.has(e) && this.store.delete(e);
945
107
  }
946
- setItem(t, e) {
947
- this.store.set(t, e);
108
+ setItem(e, t) {
109
+ this.store.set(e, t);
948
110
  }
949
111
  }
950
- function A() {
951
- return typeof window < "u" && window.localStorage ? window.localStorage : new st();
112
+ function E() {
113
+ return typeof window < "u" && window.localStorage ? window.localStorage : new _();
952
114
  }
953
- const ot = "cosec-device-id";
954
- class ht {
955
- constructor(t = ot, e = A()) {
956
- this.deviceIdKey = t, this.storage = e;
115
+ const O = "cosec-device-id";
116
+ class A {
117
+ constructor(e = O, t = E()) {
118
+ this.deviceIdKey = e, this.storage = t;
957
119
  }
958
120
  /**
959
121
  * Get the current device ID.
@@ -968,8 +130,8 @@ class ht {
968
130
  *
969
131
  * @param deviceId - The device ID to set
970
132
  */
971
- set(t) {
972
- this.storage.setItem(this.deviceIdKey, t);
133
+ set(e) {
134
+ this.storage.setItem(this.deviceIdKey, e);
973
135
  }
974
136
  /**
975
137
  * Generate a new device ID.
@@ -977,7 +139,7 @@ class ht {
977
139
  * @returns A newly generated device ID
978
140
  */
979
141
  generateDeviceId() {
980
- return O.generateId();
142
+ return I.generateId();
981
143
  }
982
144
  /**
983
145
  * Get or create a device ID.
@@ -985,8 +147,8 @@ class ht {
985
147
  * @returns The existing device ID if available, otherwise a newly generated one
986
148
  */
987
149
  getOrCreate() {
988
- let t = this.get();
989
- return t || (t = this.generateDeviceId(), this.set(t)), t;
150
+ let e = this.get();
151
+ return e || (e = this.generateDeviceId(), this.set(e)), e;
990
152
  }
991
153
  /**
992
154
  * Clear the stored device ID.
@@ -995,10 +157,10 @@ class ht {
995
157
  this.storage.removeItem(this.deviceIdKey);
996
158
  }
997
159
  }
998
- const nt = "cosec-token";
999
- class dt {
1000
- constructor(t = nt, e = A()) {
1001
- this.tokenKey = t, this.storage = e;
160
+ const p = "cosec-token";
161
+ class f {
162
+ constructor(e = p, t = E()) {
163
+ this.tokenKey = e, this.storage = t;
1002
164
  }
1003
165
  /**
1004
166
  * Get the current access token.
@@ -1006,13 +168,13 @@ class dt {
1006
168
  * @returns The current composite token or null if not set
1007
169
  */
1008
170
  get() {
1009
- const t = this.storage.getItem(this.tokenKey);
1010
- if (!t)
171
+ const e = this.storage.getItem(this.tokenKey);
172
+ if (!e)
1011
173
  return null;
1012
174
  try {
1013
- return JSON.parse(t);
1014
- } catch (e) {
1015
- return console.warn("Failed to get token from storage:", e), this.clear(), null;
175
+ return JSON.parse(e);
176
+ } catch (t) {
177
+ return console.warn("Failed to get token from storage:", t), this.clear(), null;
1016
178
  }
1017
179
  }
1018
180
  /**
@@ -1020,9 +182,9 @@ class dt {
1020
182
  *
1021
183
  * @param token - The composite token to store
1022
184
  */
1023
- set(t) {
1024
- const e = JSON.stringify(t);
1025
- this.storage.setItem(this.tokenKey, e);
185
+ set(e) {
186
+ const t = JSON.stringify(e);
187
+ this.storage.setItem(this.tokenKey, t);
1026
188
  }
1027
189
  /**
1028
190
  * Clear all tokens.
@@ -1032,21 +194,21 @@ class dt {
1032
194
  }
1033
195
  }
1034
196
  export {
1035
- ct as AuthorizeResults,
1036
- W as COSEC_REQUEST_INTERCEPTOR_NAME,
1037
- tt as COSEC_REQUEST_INTERCEPTOR_ORDER,
1038
- et as COSEC_RESPONSE_INTERCEPTOR_NAME,
1039
- rt as COSEC_RESPONSE_INTERCEPTOR_ORDER,
1040
- a as CoSecHeaders,
1041
- at as CoSecRequestInterceptor,
1042
- ut as CoSecResponseInterceptor,
1043
- ot as DEFAULT_COSEC_DEVICE_ID_KEY,
1044
- nt as DEFAULT_COSEC_TOKEN_KEY,
1045
- ht as DeviceIdStorage,
1046
- st as InMemoryStorage,
1047
- Z as NanoIdGenerator,
1048
- w as ResponseCodes,
1049
- dt as TokenStorage,
1050
- A as getStorage,
1051
- O as idGenerator
197
+ C as AuthorizeResults,
198
+ R as COSEC_REQUEST_INTERCEPTOR_NAME,
199
+ g as COSEC_REQUEST_INTERCEPTOR_ORDER,
200
+ S as COSEC_RESPONSE_INTERCEPTOR_NAME,
201
+ T as COSEC_RESPONSE_INTERCEPTOR_ORDER,
202
+ o as CoSecHeaders,
203
+ N as CoSecRequestInterceptor,
204
+ y as CoSecResponseInterceptor,
205
+ O as DEFAULT_COSEC_DEVICE_ID_KEY,
206
+ p as DEFAULT_COSEC_TOKEN_KEY,
207
+ A as DeviceIdStorage,
208
+ _ as InMemoryStorage,
209
+ l as NanoIdGenerator,
210
+ c as ResponseCodes,
211
+ f as TokenStorage,
212
+ E as getStorage,
213
+ I as idGenerator
1052
214
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(o,h){typeof exports=="object"&&typeof module<"u"?h(exports):typeof define=="function"&&define.amd?define(["exports"],h):(o=typeof globalThis<"u"?globalThis:o||self,h(o.FetcherCoSec={}))})(this,(function(o){"use strict";function h(r){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(r)}function F(r,e){return h(e)?e:e?r.replace(/\/?\/$/,"")+"/"+e.replace(/^\/+/,""):r}class L{constructor(e){this.baseURL=e}build(e,t){const s=t?.path,n=t?.query,c=F(this.baseURL,e);let i=this.interpolateUrl(c,s);if(n){const l=new URLSearchParams(n).toString();l&&(i+="?"+l)}return i}resolveRequestUrl(e){return this.build(e.url,e.urlParams)}interpolateUrl(e,t){return t?e.replace(/{([^}]+)}/g,(s,n)=>{const c=t[n];if(c===void 0)throw new Error(`Missing required path parameter: ${n}`);return String(c)}):e}}class d extends Error{constructor(e,t){const s=e||t?.message||"An error occurred in the fetcher";super(s),this.cause=t,this.name="FetcherError",t?.stack&&(this.stack=t.stack),Object.setPrototypeOf(this,d.prototype)}}class p extends d{constructor(e){const t=e.method||"GET",s=`Request timeout of ${e.timeout}ms exceeded for ${t} ${e.url}`;super(s),this.name="FetchTimeoutError",this.request=e,Object.setPrototypeOf(this,p.prototype)}}function M(r,e){return typeof r<"u"?r:e}async function G(r){const e=r.url,t=r.timeout,s=r;if(!t)return fetch(e,s);const n=new AbortController,c={...s,signal:n.signal};let i=null;const l=new Promise((Ee,de)=>{i=setTimeout(()=>{i&&clearTimeout(i);const k=new p(r);n.abort(k),de(k)},t)});try{return await Promise.race([fetch(e,c),l])}finally{i&&clearTimeout(i)}}const K="UrlResolveInterceptor",_=Number.MIN_SAFE_INTEGER+1e3;class H{constructor(){this.name=K,this.order=_}intercept(e){const t=e.request;t.url=e.fetcher.urlBuilder.resolveRequestUrl(t)}}var a=(r=>(r.GET="GET",r.POST="POST",r.PUT="PUT",r.DELETE="DELETE",r.PATCH="PATCH",r.HEAD="HEAD",r.OPTIONS="OPTIONS",r))(a||{}),I=(r=>(r.APPLICATION_JSON="application/json",r.TEXT_EVENT_STREAM="text/event-stream",r))(I||{});const $="RequestBodyInterceptor",C=_+1e3;class j{constructor(){this.name=$,this.order=C}intercept(e){const t=e.request;if(t.body===void 0||t.body===null||typeof t.body!="object"||t.body instanceof ArrayBuffer||ArrayBuffer.isView(t.body)||t.body instanceof Blob||t.body instanceof File||t.body instanceof URLSearchParams||t.body instanceof FormData||t.body instanceof ReadableStream)return;const s={...t};s.body=JSON.stringify(t.body),s.headers||(s.headers={});const n=s.headers;n["Content-Type"]||(n["Content-Type"]=I.APPLICATION_JSON),e.request=s}}const V="FetchInterceptor",z=Number.MAX_SAFE_INTEGER-1e3;class B{constructor(){this.name=V,this.order=z}async intercept(e){e.response=await G(e.request)}}function f(r,e){return e?r.filter(e).sort((t,s)=>t.order-s.order):[...r].sort((t,s)=>t.order-s.order)}class T{constructor(e=[]){this.sortedInterceptors=[],this.sortedInterceptors=f(e)}get name(){return this.constructor.name}get order(){return Number.MIN_SAFE_INTEGER}get interceptors(){return[...this.sortedInterceptors]}use(e){return this.sortedInterceptors.some(t=>t.name===e.name)?!1:(this.sortedInterceptors=f([...this.sortedInterceptors,e]),!0)}eject(e){const t=this.sortedInterceptors;return this.sortedInterceptors=f(t,s=>s.name!==e),t.length!==this.sortedInterceptors.length}clear(){this.sortedInterceptors=[]}async intercept(e){for(const t of this.sortedInterceptors)await t.intercept(e)}}class g extends d{constructor(e){super(`Request failed with status code ${e.response?.status} for ${e.request.url}`),this.exchange=e,this.name="HttpStatusValidationError",Object.setPrototypeOf(this,g.prototype)}}const J=r=>r>=200&&r<300,Q="ValidateStatusInterceptor",Y=Number.MAX_SAFE_INTEGER-1e3;class X{constructor(e=J){this.validateStatus=e}get name(){return Q}get order(){return Y}intercept(e){if(!e.response)return;const t=e.response.status;if(!this.validateStatus(t))throw new g(e)}}class E extends d{constructor(e,t){const s=t||e.error?.message||e.response?.statusText||`Request to ${e.request.url} failed during exchange`;super(s,e.error),this.exchange=e,this.name="ExchangeError",Object.setPrototypeOf(this,E.prototype)}}class Z{constructor(){this.request=new T([new H,new j,new B]),this.response=new T([new X]),this.error=new T}async exchange(e){try{return await this.request.intercept(e),await this.response.intercept(e),e}catch(t){if(e.error=t,await this.error.intercept(e),!e.hasError())return e;throw new E(e)}}}class W{constructor(e,t,s,n){this.attributes={},this.fetcher=e,this.request=t,this.response=s,this.error=n}hasError(){return!!this.error}hasResponse(){return!!this.response}get requiredResponse(){if(!this.response)throw new E(this,`Request to ${this.request.url} failed with no response`);return this.response}}function x(r,e){if(!(r===void 0&&e===void 0))return e===void 0?r:r===void 0?e:{...r,...e}}const S={"Content-Type":I.APPLICATION_JSON},w={baseURL:"",headers:S};class ee{constructor(e=w){this.headers=S,this.urlBuilder=new L(e.baseURL),this.headers=e.headers??S,this.timeout=e.timeout,this.interceptors=e.interceptors??new Z}async fetch(e,t={}){const s=t;return s.url=e,(await this.request(s)).requiredResponse}async request(e){const t=x(e.headers,this.headers),s={...e,headers:t,timeout:M(e.timeout,this.timeout)},n=new W(this,s);return this.interceptors.exchange(n)}async methodFetch(e,t,s={}){return this.fetch(t,{...s,method:e})}async get(e,t={}){return this.methodFetch(a.GET,e,t)}async post(e,t={}){return this.methodFetch(a.POST,e,t)}async put(e,t={}){return this.methodFetch(a.PUT,e,t)}async delete(e,t={}){return this.methodFetch(a.DELETE,e,t)}async patch(e,t={}){return this.methodFetch(a.PATCH,e,t)}async head(e,t={}){return this.methodFetch(a.HEAD,e,t)}async options(e,t={}){return this.methodFetch(a.OPTIONS,e,t)}}const R="default";class te{constructor(){this.registrar=new Map}register(e,t){this.registrar.set(e,t)}unregister(e){return this.registrar.delete(e)}get(e){return this.registrar.get(e)}requiredGet(e){const t=this.get(e);if(!t)throw new Error(`Fetcher ${e} not found`);return t}get default(){return this.requiredGet(R)}set default(e){this.register(R,e)}get fetchers(){return new Map(this.registrar)}}const re=new te;class se extends ee{constructor(e,t=w){super(t),this.name=e,re.register(e,this)}}new se(R);var u=(r=>(r.DEVICE_ID="CoSec-Device-Id",r.APP_ID="CoSec-App-Id",r.AUTHORIZATION="Authorization",r.REQUEST_ID="CoSec-Request-Id",r))(u||{}),m=(r=>(r[r.UNAUTHORIZED=401]="UNAUTHORIZED",r))(m||{});const oe={ALLOW:{authorized:!0,reason:"Allow"},EXPLICIT_DENY:{authorized:!1,reason:"Explicit Deny"},IMPLICIT_DENY:{authorized:!1,reason:"Implicit Deny"},TOKEN_EXPIRED:{authorized:!1,reason:"Token Expired"},TOO_MANY_REQUESTS:{authorized:!1,reason:"Too Many Requests"}},ne="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let ie=(r=21)=>{let e="",t=crypto.getRandomValues(new Uint8Array(r|=0));for(;r--;)e+=ne[t[r]&63];return e};class N{generateId(){return ie()}}const y=new N,A="CoSecRequestInterceptor",P=C+1e3;class ce{constructor(e){this.name=A,this.order=P,this.options=e}intercept(e){const t=y.generateId(),s=this.options.deviceIdStorage.getOrCreate(),n=this.options.tokenStorage.get(),c={...e.request,headers:{...e.request.headers}},i=c.headers;i[u.APP_ID]=this.options.appId,i[u.DEVICE_ID]=s,i[u.REQUEST_ID]=t,n&&(i[u.AUTHORIZATION]=`Bearer ${n.accessToken}`),e.request=c}}const D="CoSecResponseInterceptor",q=Number.MIN_SAFE_INTEGER+1e3;class ae{constructor(e){this.name=D,this.order=q,this.options=e}async intercept(e){const t=e.response;if(!t||t.status!==m.UNAUTHORIZED)return;const s=this.options.tokenStorage.get();if(s)try{const n=await this.options.tokenRefresher.refresh(s);this.options.tokenStorage.set(n),await e.fetcher.request(e.request)}catch(n){throw this.options.tokenStorage.clear(),n}}}class b{constructor(){this.store=new Map}get length(){return this.store.size}clear(){this.store.clear()}getItem(e){const t=this.store.get(e);return t!==void 0?t:null}key(e){return Array.from(this.store.keys())[e]||null}removeItem(e){this.store.has(e)&&this.store.delete(e)}setItem(e,t){this.store.set(e,t)}}function O(){return typeof window<"u"&&window.localStorage?window.localStorage:new b}const v="cosec-device-id";class ue{constructor(e=v,t=O()){this.deviceIdKey=e,this.storage=t}get(){return this.storage.getItem(this.deviceIdKey)}set(e){this.storage.setItem(this.deviceIdKey,e)}generateDeviceId(){return y.generateId()}getOrCreate(){let e=this.get();return e||(e=this.generateDeviceId(),this.set(e)),e}clear(){this.storage.removeItem(this.deviceIdKey)}}const U="cosec-token";class he{constructor(e=U,t=O()){this.tokenKey=e,this.storage=t}get(){const e=this.storage.getItem(this.tokenKey);if(!e)return null;try{return JSON.parse(e)}catch(t){return console.warn("Failed to get token from storage:",t),this.clear(),null}}set(e){const t=JSON.stringify(e);this.storage.setItem(this.tokenKey,t)}clear(){this.storage.removeItem(this.tokenKey)}}o.AuthorizeResults=oe,o.COSEC_REQUEST_INTERCEPTOR_NAME=A,o.COSEC_REQUEST_INTERCEPTOR_ORDER=P,o.COSEC_RESPONSE_INTERCEPTOR_NAME=D,o.COSEC_RESPONSE_INTERCEPTOR_ORDER=q,o.CoSecHeaders=u,o.CoSecRequestInterceptor=ce,o.CoSecResponseInterceptor=ae,o.DEFAULT_COSEC_DEVICE_ID_KEY=v,o.DEFAULT_COSEC_TOKEN_KEY=U,o.DeviceIdStorage=ue,o.InMemoryStorage=b,o.NanoIdGenerator=N,o.ResponseCodes=m,o.TokenStorage=he,o.getStorage=O,o.idGenerator=y,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(t,i){typeof exports=="object"&&typeof module<"u"?i(exports,require("@ahoo-wang/fetcher")):typeof define=="function"&&define.amd?define(["exports","@ahoo-wang/fetcher"],i):(t=typeof globalThis<"u"?globalThis:t||self,i(t.FetcherCoSec={},t.Fetcher))})(this,(function(t,i){"use strict";var s=(o=>(o.DEVICE_ID="CoSec-Device-Id",o.APP_ID="CoSec-App-Id",o.AUTHORIZATION="Authorization",o.REQUEST_ID="CoSec-Request-Id",o))(s||{}),E=(o=>(o[o.UNAUTHORIZED=401]="UNAUTHORIZED",o))(E||{});const g={ALLOW:{authorized:!0,reason:"Allow"},EXPLICIT_DENY:{authorized:!1,reason:"Explicit Deny"},IMPLICIT_DENY:{authorized:!1,reason:"Implicit Deny"},TOKEN_EXPIRED:{authorized:!1,reason:"Token Expired"},TOO_MANY_REQUESTS:{authorized:!1,reason:"Too Many Requests"}},f="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let D=(o=21)=>{let e="",r=crypto.getRandomValues(new Uint8Array(o|=0));for(;o--;)e+=f[r[o]&63];return e};class h{generateId(){return D()}}const I=new h,d="CoSecRequestInterceptor",R=i.REQUEST_BODY_INTERCEPTOR_ORDER+1e3;class N{constructor(e){this.name=d,this.order=R,this.options=e}intercept(e){const r=I.generateId(),a=this.options.deviceIdStorage.getOrCreate(),n=this.options.tokenStorage.get(),O={...e.request,headers:{...e.request.headers}},c=O.headers;c[s.APP_ID]=this.options.appId,c[s.DEVICE_ID]=a,c[s.REQUEST_ID]=r,n&&(c[s.AUTHORIZATION]=`Bearer ${n.accessToken}`),e.request=O}}const S="CoSecResponseInterceptor",T=Number.MIN_SAFE_INTEGER+1e3;class y{constructor(e){this.name=S,this.order=T,this.options=e}async intercept(e){const r=e.response;if(!r||r.status!==E.UNAUTHORIZED)return;const a=this.options.tokenStorage.get();if(a)try{const n=await this.options.tokenRefresher.refresh(a);this.options.tokenStorage.set(n),await e.fetcher.request(e.request)}catch(n){throw this.options.tokenStorage.clear(),n}}}class _{constructor(){this.store=new Map}get length(){return this.store.size}clear(){this.store.clear()}getItem(e){const r=this.store.get(e);return r!==void 0?r:null}key(e){return Array.from(this.store.keys())[e]||null}removeItem(e){this.store.has(e)&&this.store.delete(e)}setItem(e,r){this.store.set(e,r)}}function u(){return typeof window<"u"&&window.localStorage?window.localStorage:new _}const l="cosec-device-id";class A{constructor(e=l,r=u()){this.deviceIdKey=e,this.storage=r}get(){return this.storage.getItem(this.deviceIdKey)}set(e){this.storage.setItem(this.deviceIdKey,e)}generateDeviceId(){return I.generateId()}getOrCreate(){let e=this.get();return e||(e=this.generateDeviceId(),this.set(e)),e}clear(){this.storage.removeItem(this.deviceIdKey)}}const C="cosec-token";class p{constructor(e=C,r=u()){this.tokenKey=e,this.storage=r}get(){const e=this.storage.getItem(this.tokenKey);if(!e)return null;try{return JSON.parse(e)}catch(r){return console.warn("Failed to get token from storage:",r),this.clear(),null}}set(e){const r=JSON.stringify(e);this.storage.setItem(this.tokenKey,r)}clear(){this.storage.removeItem(this.tokenKey)}}t.AuthorizeResults=g,t.COSEC_REQUEST_INTERCEPTOR_NAME=d,t.COSEC_REQUEST_INTERCEPTOR_ORDER=R,t.COSEC_RESPONSE_INTERCEPTOR_NAME=S,t.COSEC_RESPONSE_INTERCEPTOR_ORDER=T,t.CoSecHeaders=s,t.CoSecRequestInterceptor=N,t.CoSecResponseInterceptor=y,t.DEFAULT_COSEC_DEVICE_ID_KEY=l,t.DEFAULT_COSEC_TOKEN_KEY=C,t.DeviceIdStorage=A,t.InMemoryStorage=_,t.NanoIdGenerator=h,t.ResponseCodes=E,t.TokenStorage=p,t.getStorage=u,t.idGenerator=I,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahoo-wang/fetcher-cosec",
3
- "version": "0.9.6",
3
+ "version": "0.9.8",
4
4
  "description": "CoSec authentication integration for Fetcher HTTP client",
5
5
  "keywords": [
6
6
  "fetch",
@@ -36,7 +36,7 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "nanoid": "^5.1.5",
39
- "@ahoo-wang/fetcher": "0.9.6"
39
+ "@ahoo-wang/fetcher": "0.9.8"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@vitest/coverage-v8": "^3.2.4",