@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 +83 -921
- package/dist/index.umd.js +1 -1
- package/package.json +2 -2
package/dist/index.es.js
CHANGED
|
@@ -1,870 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
},
|
|
848
|
-
let
|
|
849
|
-
let
|
|
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
|
-
|
|
852
|
-
return
|
|
13
|
+
e += u[t[r] & 63];
|
|
14
|
+
return e;
|
|
853
15
|
};
|
|
854
|
-
class
|
|
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
|
|
23
|
+
return d();
|
|
862
24
|
}
|
|
863
25
|
}
|
|
864
|
-
const
|
|
865
|
-
class
|
|
866
|
-
constructor(
|
|
867
|
-
this.name =
|
|
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(
|
|
890
|
-
const
|
|
891
|
-
...
|
|
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
|
-
...
|
|
55
|
+
...e.request.headers
|
|
894
56
|
}
|
|
895
|
-
},
|
|
896
|
-
|
|
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
|
|
900
|
-
class
|
|
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(
|
|
906
|
-
this.name =
|
|
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(
|
|
913
|
-
const
|
|
914
|
-
if (!
|
|
74
|
+
async intercept(e) {
|
|
75
|
+
const t = e.response;
|
|
76
|
+
if (!t || t.status !== c.UNAUTHORIZED)
|
|
915
77
|
return;
|
|
916
|
-
const
|
|
917
|
-
if (
|
|
78
|
+
const n = this.options.tokenStorage.get();
|
|
79
|
+
if (n)
|
|
918
80
|
try {
|
|
919
|
-
const
|
|
920
|
-
this.options.tokenStorage.set(
|
|
921
|
-
} catch (
|
|
922
|
-
throw this.options.tokenStorage.clear(),
|
|
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
|
|
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(
|
|
937
|
-
const
|
|
938
|
-
return
|
|
98
|
+
getItem(e) {
|
|
99
|
+
const t = this.store.get(e);
|
|
100
|
+
return t !== void 0 ? t : null;
|
|
939
101
|
}
|
|
940
|
-
key(
|
|
941
|
-
return Array.from(this.store.keys())[
|
|
102
|
+
key(e) {
|
|
103
|
+
return Array.from(this.store.keys())[e] || null;
|
|
942
104
|
}
|
|
943
|
-
removeItem(
|
|
944
|
-
this.store.has(
|
|
105
|
+
removeItem(e) {
|
|
106
|
+
this.store.has(e) && this.store.delete(e);
|
|
945
107
|
}
|
|
946
|
-
setItem(
|
|
947
|
-
this.store.set(
|
|
108
|
+
setItem(e, t) {
|
|
109
|
+
this.store.set(e, t);
|
|
948
110
|
}
|
|
949
111
|
}
|
|
950
|
-
function
|
|
951
|
-
return typeof window < "u" && window.localStorage ? window.localStorage : new
|
|
112
|
+
function E() {
|
|
113
|
+
return typeof window < "u" && window.localStorage ? window.localStorage : new _();
|
|
952
114
|
}
|
|
953
|
-
const
|
|
954
|
-
class
|
|
955
|
-
constructor(
|
|
956
|
-
this.deviceIdKey =
|
|
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(
|
|
972
|
-
this.storage.setItem(this.deviceIdKey,
|
|
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
|
|
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
|
|
989
|
-
return
|
|
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
|
|
999
|
-
class
|
|
1000
|
-
constructor(
|
|
1001
|
-
this.tokenKey =
|
|
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
|
|
1010
|
-
if (!
|
|
171
|
+
const e = this.storage.getItem(this.tokenKey);
|
|
172
|
+
if (!e)
|
|
1011
173
|
return null;
|
|
1012
174
|
try {
|
|
1013
|
-
return JSON.parse(
|
|
1014
|
-
} catch (
|
|
1015
|
-
return console.warn("Failed to get token from storage:",
|
|
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(
|
|
1024
|
-
const
|
|
1025
|
-
this.storage.setItem(this.tokenKey,
|
|
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
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
39
|
+
"@ahoo-wang/fetcher": "0.9.8"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@vitest/coverage-v8": "^3.2.4",
|