vigor-fetch 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.d.ts +237 -196
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +275 -272
- package/dist/index.mjs +275 -272
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,158 +2,144 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
const VIGOR_ERROR_MESSAGES = {
|
|
6
|
+
TIMEOUT: ({ limit, attempt }) => `Timeout: exceeded ${limit}ms (attempt: ${attempt})`,
|
|
7
|
+
EXHAUSTED: ({ maxAttempts }) => `Retry exhausted: max ${maxAttempts})`,
|
|
8
|
+
INVALID_URL: ({ received }) => `Invalid URL: ${received}`,
|
|
9
|
+
INVALID_PROTOCOL: ({ expected, received }) => `Invalid protocol: ${received} (expected ${expected.join(", ")})`,
|
|
10
|
+
FETCH_ERROR: ({ status, statusText, url }) => `HTTP Error: ${status} ${statusText} (url: ${url})`,
|
|
11
|
+
PARSE_FAILED: ({ expected }) => `Parse failed: expected ${expected}`,
|
|
12
|
+
INVALID_TYPE: ({ expected, received }) => `Invalid parser type: ${expected}`,
|
|
13
|
+
TARGET_MISSING: () => `Target missing`,
|
|
14
|
+
REQUEST_FAILED: ({ index, error }) => `Request failed at index ${index}: ${error.message}`,
|
|
15
|
+
UNKNOWN: () => `Unknown error`
|
|
16
|
+
};
|
|
5
17
|
class VigorError extends Error {
|
|
6
|
-
timestamp;
|
|
18
|
+
timestamp = new Date();
|
|
7
19
|
method;
|
|
20
|
+
code;
|
|
8
21
|
cause;
|
|
9
22
|
context;
|
|
10
23
|
type;
|
|
11
24
|
data;
|
|
12
|
-
constructor(
|
|
25
|
+
constructor(code, options) {
|
|
26
|
+
const messageFn = VIGOR_ERROR_MESSAGES[code];
|
|
27
|
+
const message = `[${code}] ${messageFn(options?.data)}`;
|
|
13
28
|
super(message, { cause: options?.cause });
|
|
14
29
|
this.name = new.target.name;
|
|
15
|
-
this.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (options?.type !== undefined)
|
|
21
|
-
this.type = options.type;
|
|
22
|
-
if (options?.data !== undefined)
|
|
23
|
-
this.data = options.data;
|
|
30
|
+
this.method = options?.method;
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.context = options?.context;
|
|
33
|
+
this.type = options?.type;
|
|
34
|
+
this.data = options.data;
|
|
24
35
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
25
36
|
Error.captureStackTrace?.(this, new.target);
|
|
26
37
|
}
|
|
27
38
|
}
|
|
28
39
|
class VigorRetryError extends VigorError {
|
|
29
|
-
constructor(
|
|
30
|
-
super(
|
|
40
|
+
constructor(code, options) {
|
|
41
|
+
super(code, options);
|
|
31
42
|
}
|
|
32
43
|
}
|
|
33
44
|
class VigorParseError extends VigorError {
|
|
34
|
-
constructor(
|
|
35
|
-
super(
|
|
45
|
+
constructor(code, options) {
|
|
46
|
+
super(code, options);
|
|
36
47
|
}
|
|
37
48
|
}
|
|
38
49
|
class VigorFetchError extends VigorError {
|
|
39
|
-
constructor(
|
|
40
|
-
super(
|
|
50
|
+
constructor(code, options) {
|
|
51
|
+
super(code, options);
|
|
41
52
|
}
|
|
42
53
|
}
|
|
43
54
|
class VigorAllError extends VigorError {
|
|
44
|
-
constructor(
|
|
45
|
-
super(
|
|
55
|
+
constructor(code, options) {
|
|
56
|
+
super(code, options);
|
|
46
57
|
}
|
|
47
58
|
}
|
|
59
|
+
const EMPTY = Symbol("EMPTY");
|
|
48
60
|
class VigorStatus {
|
|
61
|
+
_base;
|
|
49
62
|
_config;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this._ctor = _ctor;
|
|
55
|
-
this._errorCtor = _errorCtor;
|
|
56
|
-
}
|
|
57
|
-
_create(config) { return this._ctor(config); }
|
|
58
|
-
_next(config) { return this._create({ ...this._config, ...config }); }
|
|
63
|
+
constructor(config, _base) {
|
|
64
|
+
this._base = _base;
|
|
65
|
+
this._config = { ...this._base, ...config };
|
|
66
|
+
}
|
|
59
67
|
getConfig() { return this._config; }
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
method: errorKey,
|
|
65
|
-
type: "invalid_input",
|
|
66
|
-
data: { expected: "function", received: fn }
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return fn(new Ctor(value)).getConfig();
|
|
68
|
+
getBase() { return this._base; }
|
|
69
|
+
_next(config) { return new this.constructor({ ...this._config, ...config }, this._base); }
|
|
70
|
+
_pipsub(config, fn, ctor) {
|
|
71
|
+
return fn(new ctor(config)).getConfig();
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
class VigorRetrySettings extends VigorStatus {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const base = {
|
|
75
|
+
constructor(config = {}) {
|
|
76
|
+
super(config, {
|
|
76
77
|
count: 5,
|
|
77
78
|
limit: 10000,
|
|
78
79
|
maxDelay: 10000,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this._base = base;
|
|
80
|
+
default: EMPTY
|
|
81
|
+
});
|
|
82
82
|
}
|
|
83
|
-
getBase() { return this._base; }
|
|
84
83
|
count(num) { return this._next({ count: num }); }
|
|
85
84
|
limit(num) { return this._next({ limit: num }); }
|
|
86
85
|
maxDelay(num) { return this._next({ maxDelay: num }); }
|
|
87
86
|
default(obj) { return this._next({ default: obj }); }
|
|
88
87
|
}
|
|
89
88
|
class VigorRetryBackoff extends VigorStatus {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const base = {
|
|
89
|
+
constructor(config = {}) {
|
|
90
|
+
super(config, {
|
|
93
91
|
initialDelay: 0,
|
|
94
92
|
baseDelay: 1000,
|
|
95
93
|
factor: 1.7,
|
|
96
94
|
jitter: 1000
|
|
97
|
-
};
|
|
98
|
-
super({ ...base, ...config }, (c) => new VigorRetryBackoff(c));
|
|
99
|
-
this._base = base;
|
|
95
|
+
});
|
|
100
96
|
}
|
|
101
|
-
getBase() { return this._base; }
|
|
102
97
|
initialDelay(num) { return this._next({ initialDelay: num }); }
|
|
103
98
|
baseDelay(num) { return this._next({ baseDelay: num }); }
|
|
104
99
|
factor(num) { return this._next({ factor: num }); }
|
|
105
100
|
jitter(num) { return this._next({ jitter: num }); }
|
|
101
|
+
static randomJitter(num) { return Math.random() * num; }
|
|
106
102
|
}
|
|
107
103
|
class VigorRetryInterceptors extends VigorStatus {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const base = {
|
|
104
|
+
constructor(config = {}) {
|
|
105
|
+
super(config, {
|
|
111
106
|
before: [],
|
|
112
107
|
after: [],
|
|
113
108
|
onError: [],
|
|
114
109
|
onRetry: [],
|
|
115
110
|
retryIf: []
|
|
116
|
-
};
|
|
117
|
-
super({ ...base, ...config }, (c) => new VigorRetryInterceptors(c));
|
|
118
|
-
this._base = base;
|
|
111
|
+
});
|
|
119
112
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
retryIf(...funcs) { return this._next({ retryIf: [...this.getConfig().retryIf, ...funcs.flat()] }); }
|
|
113
|
+
before(...funcs) { return this._next({ ...this._config, before: [...this._config.before, ...funcs.flat()] }); }
|
|
114
|
+
after(...funcs) { return this._next({ ...this._config, after: [...this._config.after, ...funcs.flat()] }); }
|
|
115
|
+
onError(...funcs) { return this._next({ ...this._config, onError: [...this._config.onError, ...funcs.flat()] }); }
|
|
116
|
+
onRetry(...funcs) { return this._next({ ...this._config, onRetry: [...this._config.onRetry, ...funcs.flat()] }); }
|
|
117
|
+
retryIf(...funcs) { return this._next({ ...this._config, retryIf: [...this._config.retryIf, ...funcs.flat()] }); }
|
|
126
118
|
}
|
|
127
119
|
class VigorRetry extends VigorStatus {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
constructor(config) {
|
|
131
|
-
const base = {
|
|
120
|
+
constructor(config = {}) {
|
|
121
|
+
super(config, {
|
|
132
122
|
target: null,
|
|
133
123
|
setting: new VigorRetrySettings().getBase(),
|
|
134
124
|
backoff: new VigorRetryBackoff().getBase(),
|
|
135
|
-
interceptors: new VigorRetryInterceptors().getBase()
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this._base = base;
|
|
125
|
+
interceptors: new VigorRetryInterceptors().getBase(),
|
|
126
|
+
controller: config.controller || new AbortController()
|
|
127
|
+
});
|
|
139
128
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
129
|
+
_transfer(config) { return new VigorRetry({ ...this._config, ...config }); }
|
|
130
|
+
_calculateDelay(initialDelay, baseDelay, factor, attempt, jitter, maxDelay) {
|
|
131
|
+
return Math.max(0, Math.min(maxDelay, initialDelay + baseDelay * Math.pow(factor, attempt) + VigorRetryBackoff.randomJitter(jitter)));
|
|
132
|
+
}
|
|
133
|
+
createController() { return this._config.controller = new AbortController(); }
|
|
134
|
+
target(func) { return this._transfer({ target: func }); }
|
|
143
135
|
setting(func) {
|
|
144
|
-
return this._next({
|
|
145
|
-
setting: this._pipeSub(this._config.setting, VigorRetrySettings, func, "setting")
|
|
146
|
-
});
|
|
136
|
+
return this._next({ setting: this._pipsub(this._config.setting, func, VigorRetrySettings) });
|
|
147
137
|
}
|
|
148
138
|
backoff(func) {
|
|
149
|
-
return this._next({
|
|
150
|
-
backoff: this._pipeSub(this._config.backoff, VigorRetryBackoff, func, "backoff")
|
|
151
|
-
});
|
|
139
|
+
return this._next({ backoff: this._pipsub(this._config.backoff, func, VigorRetryBackoff) });
|
|
152
140
|
}
|
|
153
141
|
interceptors(func) {
|
|
154
|
-
return this._next({
|
|
155
|
-
interceptors: this._pipeSub(this._config.interceptors, VigorRetryInterceptors, func, "interceptors")
|
|
156
|
-
});
|
|
142
|
+
return this._next({ interceptors: this._pipsub(this._config.interceptors, func, VigorRetryInterceptors) });
|
|
157
143
|
}
|
|
158
144
|
async request() {
|
|
159
145
|
const config = this._config;
|
|
@@ -168,8 +154,9 @@ class VigorRetry extends VigorStatus {
|
|
|
168
154
|
retryIf: [...config.interceptors.retryIf],
|
|
169
155
|
},
|
|
170
156
|
backoff: { ...config.backoff },
|
|
157
|
+
controller: config.controller,
|
|
171
158
|
runtime: {
|
|
172
|
-
result:
|
|
159
|
+
result: EMPTY,
|
|
173
160
|
controller: null,
|
|
174
161
|
attempt: 0,
|
|
175
162
|
aborted: false,
|
|
@@ -187,7 +174,6 @@ class VigorRetry extends VigorStatus {
|
|
|
187
174
|
};
|
|
188
175
|
try {
|
|
189
176
|
while (ctx.runtime.attempt < ctx.setting.count) {
|
|
190
|
-
ctx.runtime.attempt++;
|
|
191
177
|
ctx.runtime.controller = new AbortController();
|
|
192
178
|
let listener;
|
|
193
179
|
let timerId;
|
|
@@ -197,7 +183,7 @@ class VigorRetry extends VigorStatus {
|
|
|
197
183
|
} };
|
|
198
184
|
try {
|
|
199
185
|
ctx.runtime.signal = AbortSignal.any([
|
|
200
|
-
|
|
186
|
+
ctx.controller.signal,
|
|
201
187
|
ctx.runtime.controller.signal
|
|
202
188
|
]);
|
|
203
189
|
ctx.runtime.abortPromise = new Promise((_, reject) => {
|
|
@@ -211,7 +197,14 @@ class VigorRetry extends VigorStatus {
|
|
|
211
197
|
timerId = setTimeout(() => {
|
|
212
198
|
if (ctx.runtime.aborted)
|
|
213
199
|
return;
|
|
214
|
-
abort(new VigorRetryError(
|
|
200
|
+
abort(new VigorRetryError("TIMEOUT", {
|
|
201
|
+
method: "request",
|
|
202
|
+
type: "timeout",
|
|
203
|
+
data: {
|
|
204
|
+
limit: ctx.setting.limit,
|
|
205
|
+
attempt: ctx.runtime.attempt
|
|
206
|
+
}
|
|
207
|
+
}));
|
|
215
208
|
}, ctx.setting.limit);
|
|
216
209
|
});
|
|
217
210
|
for (const func of ctx.interceptors.before) {
|
|
@@ -244,7 +237,8 @@ class VigorRetry extends VigorStatus {
|
|
|
244
237
|
if (!ctx.runtime.retry) {
|
|
245
238
|
throw ctx.runtime.error;
|
|
246
239
|
}
|
|
247
|
-
|
|
240
|
+
const { initialDelay, baseDelay, factor, jitter } = ctx.backoff;
|
|
241
|
+
ctx.runtime.delay = this._calculateDelay(initialDelay, baseDelay, factor, ctx.runtime.attempt, jitter, ctx.setting.maxDelay);
|
|
248
242
|
const setDelay = (delay) => ctx.runtime.delay = delay;
|
|
249
243
|
for (const func of ctx.interceptors.onRetry) {
|
|
250
244
|
await func(ctx, { setAttempt, throwError, setDelay });
|
|
@@ -253,11 +247,11 @@ class VigorRetry extends VigorStatus {
|
|
|
253
247
|
const timer = setTimeout(resolve, ctx.runtime.delay);
|
|
254
248
|
const abortHandler = () => {
|
|
255
249
|
clearTimeout(timer);
|
|
256
|
-
reject(
|
|
250
|
+
reject(ctx.controller.signal.reason);
|
|
257
251
|
};
|
|
258
|
-
if (
|
|
252
|
+
if (ctx.controller.signal.aborted)
|
|
259
253
|
return abortHandler();
|
|
260
|
-
|
|
254
|
+
ctx.controller.signal.addEventListener("abort", abortHandler, { once: true });
|
|
261
255
|
});
|
|
262
256
|
}
|
|
263
257
|
finally {
|
|
@@ -265,8 +259,15 @@ class VigorRetry extends VigorStatus {
|
|
|
265
259
|
if (listener)
|
|
266
260
|
ctx.runtime.signal.removeEventListener("abort", listener);
|
|
267
261
|
}
|
|
262
|
+
ctx.runtime.attempt++;
|
|
268
263
|
}
|
|
269
|
-
throw new VigorRetryError(
|
|
264
|
+
throw new VigorRetryError("EXHAUSTED", {
|
|
265
|
+
method: "request",
|
|
266
|
+
type: "retry",
|
|
267
|
+
data: {
|
|
268
|
+
maxAttempts: ctx.setting.count,
|
|
269
|
+
}
|
|
270
|
+
});
|
|
270
271
|
}
|
|
271
272
|
catch (error) {
|
|
272
273
|
ctx.runtime.error = error;
|
|
@@ -275,45 +276,44 @@ class VigorRetry extends VigorStatus {
|
|
|
275
276
|
for (const func of ctx.interceptors.onError) {
|
|
276
277
|
await func(ctx, { setResult, throwError });
|
|
277
278
|
}
|
|
278
|
-
if (overrided && ctx.runtime.result !==
|
|
279
|
+
if (overrided && ctx.runtime.result !== EMPTY)
|
|
279
280
|
return ctx.runtime.result;
|
|
280
|
-
if (ctx.setting.default !==
|
|
281
|
+
if (ctx.setting.default !== EMPTY)
|
|
281
282
|
return ctx.setting.default;
|
|
282
283
|
throw error;
|
|
283
284
|
}
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
|
-
const basic = { key: /text/, parse: (res) => res.text(), type: "text" };
|
|
287
|
-
const parser = [
|
|
288
|
-
{ key: /json/, parse: (res) => res.json(), type: "json" },
|
|
289
|
-
{ key: /multipart\/form-data/, parse: (res) => res.formData(), type: "formData" },
|
|
290
|
-
{ key: /octet-stream/, parse: (res) => res.arrayBuffer(), type: "arrayBuffer" },
|
|
291
|
-
{ key: /(image|video|audio|pdf)/, parse: (res) => res.blob(), type: "blob" },
|
|
292
|
-
basic
|
|
293
|
-
];
|
|
294
|
-
const supported = parser.map(i => i.type);
|
|
295
287
|
class VigorParse extends VigorStatus {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const base = {
|
|
288
|
+
constructor(config = {}) {
|
|
289
|
+
super(config, {
|
|
299
290
|
original: false
|
|
300
|
-
};
|
|
301
|
-
super({ ...base, ...config }, (c) => new VigorParse(c));
|
|
302
|
-
this._base = base;
|
|
291
|
+
});
|
|
303
292
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
293
|
+
static stategy = [
|
|
294
|
+
{ key: /text/, parse: (res) => res.text(), type: "text" },
|
|
295
|
+
{ key: /json/, parse: (res) => res.json(), type: "json" },
|
|
296
|
+
{ key: /multipart\/form-data/, parse: (res) => res.formData(), type: "formData" },
|
|
297
|
+
{ key: /octet-stream/, parse: (res) => res.arrayBuffer(), type: "arrayBuffer" },
|
|
298
|
+
{ key: /(image|video|audio|pdf)/, parse: (res) => res.blob(), type: "blob" },
|
|
299
|
+
];
|
|
300
|
+
static supported = this.stategy.map(i => i.type);
|
|
301
|
+
_transfer(config) { return new VigorParse({ ...this._config, ...config }); }
|
|
302
|
+
target(res) { return this._next({ target: res }); }
|
|
303
|
+
original(bool) { return this._transfer({ ...this._config, original: bool }); }
|
|
304
|
+
type(type) { return this._transfer({ ...this._config, result: undefined, type }); }
|
|
308
305
|
async request() {
|
|
309
306
|
const config = this._config;
|
|
310
|
-
if (!config.target)
|
|
311
|
-
throw new VigorParseError("
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
307
|
+
if (!(config.target instanceof Response)) {
|
|
308
|
+
throw new VigorParseError("TARGET_MISSING", {
|
|
309
|
+
method: "request",
|
|
310
|
+
type: "args_missing",
|
|
311
|
+
data: undefined
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (config.original) {
|
|
316
315
|
return config.target;
|
|
316
|
+
}
|
|
317
317
|
const contentType = config.target.headers.get("Content-Type") || "";
|
|
318
318
|
let strategy;
|
|
319
319
|
try {
|
|
@@ -321,44 +321,42 @@ class VigorParse extends VigorStatus {
|
|
|
321
321
|
strategy = { type: config.type };
|
|
322
322
|
const parser = config.target[config.type];
|
|
323
323
|
if (!parser || typeof parser !== 'function')
|
|
324
|
-
throw new VigorParseError(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
324
|
+
throw new VigorParseError("PARSE_FAILED", {
|
|
325
|
+
method: "request",
|
|
326
|
+
type: "parse_failed",
|
|
327
|
+
data: {
|
|
328
|
+
expected: strategy?.type ?? "unknown"
|
|
329
|
+
}
|
|
330
|
+
});
|
|
330
331
|
return await parser();
|
|
331
332
|
}
|
|
332
|
-
strategy =
|
|
333
|
+
strategy = VigorParse.stategy.find(i => i.key.test(contentType)) ?? VigorParse.stategy[0];
|
|
333
334
|
return await strategy.parse(config.target);
|
|
334
335
|
}
|
|
335
336
|
catch (error) {
|
|
336
337
|
if (error instanceof VigorParseError)
|
|
337
338
|
throw error;
|
|
338
|
-
throw new VigorParseError(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
339
|
+
throw new VigorParseError("PARSE_FAILED", {
|
|
340
|
+
method: "request",
|
|
341
|
+
type: "parse_failed",
|
|
342
|
+
data: {
|
|
343
|
+
expected: strategy?.type ?? "unknown"
|
|
344
|
+
}
|
|
345
|
+
});
|
|
345
346
|
}
|
|
346
347
|
}
|
|
347
348
|
}
|
|
348
349
|
class VigorFetchSettings extends VigorStatus {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const base = {
|
|
350
|
+
constructor(config = {}) {
|
|
351
|
+
super(config, {
|
|
352
352
|
origin: "",
|
|
353
353
|
path: [],
|
|
354
354
|
query: {},
|
|
355
355
|
unretry: [400, 401, 403, 404, 405, 413, 422],
|
|
356
356
|
retryHeaders: ["retry-after", "ratelimit-reset", "x-ratelimit-reset", "x-retry-after", "x-amz-retry-after", "chrome-proxy-next-link"],
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
this._base = base;
|
|
357
|
+
default: EMPTY
|
|
358
|
+
});
|
|
360
359
|
}
|
|
361
|
-
getBase() { return this._base; }
|
|
362
360
|
origin(str) { return this._next({ origin: str }); }
|
|
363
361
|
path(...strs) { return this._next({ path: [...this._config.path, ...strs.flat()] }); }
|
|
364
362
|
query(obj) { return this._next({ query: { ...this._config.query, ...obj } }); }
|
|
@@ -371,36 +369,28 @@ class VigorFetchSettings extends VigorStatus {
|
|
|
371
369
|
default(obj) { return this._next({ default: obj }); }
|
|
372
370
|
}
|
|
373
371
|
class VigorFetchInterceptors extends VigorStatus {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const base = {
|
|
372
|
+
constructor(config = {}) {
|
|
373
|
+
super(config, {
|
|
377
374
|
before: [],
|
|
378
375
|
after: [],
|
|
379
376
|
onError: [],
|
|
380
377
|
result: []
|
|
381
|
-
};
|
|
382
|
-
super({ ...base, ...config }, (c) => new VigorFetchInterceptors(c));
|
|
383
|
-
this._base = base;
|
|
378
|
+
});
|
|
384
379
|
}
|
|
385
|
-
getBase() { return this._base; }
|
|
386
380
|
before(...funcs) { return this._next({ before: [...this.getConfig().before, ...funcs.flat()] }); }
|
|
387
381
|
after(...funcs) { return this._next({ after: [...this.getConfig().after, ...funcs.flat()] }); }
|
|
388
382
|
onError(...funcs) { return this._next({ onError: [...this.getConfig().onError, ...funcs.flat()] }); }
|
|
389
383
|
result(...funcs) { return this._next({ result: [...this.getConfig().result, ...funcs.flat()] }); }
|
|
390
384
|
}
|
|
391
385
|
class VigorFetch extends VigorStatus {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const base = {
|
|
386
|
+
constructor(config = {}) {
|
|
387
|
+
super(config, {
|
|
395
388
|
setting: new VigorFetchSettings().getBase(),
|
|
396
389
|
retryConfig: new VigorRetry().getBase(),
|
|
397
390
|
parseConfig: new VigorParse().getBase(),
|
|
398
391
|
interceptors: new VigorFetchInterceptors().getBase(),
|
|
399
|
-
};
|
|
400
|
-
super({ ...base, ...config }, (c) => new VigorFetch(c), () => VigorRetryError);
|
|
401
|
-
this._base = base;
|
|
392
|
+
});
|
|
402
393
|
}
|
|
403
|
-
getBase() { return this._base; }
|
|
404
394
|
origin(str) { return this._next({ setting: { ...this._config.setting, origin: str } }); }
|
|
405
395
|
path(...strs) { return this._next({ setting: { ...this._config.setting, path: [...this._config.setting.path, ...strs.flat()] } }); }
|
|
406
396
|
query(obj) { return this._next({ setting: { ...this._config.setting, query: { ...this._config.setting.query, ...obj } } }); }
|
|
@@ -409,63 +399,48 @@ class VigorFetch extends VigorStatus {
|
|
|
409
399
|
body(obj) { return this._next({ setting: { ...this._config.setting, body: obj } }); }
|
|
410
400
|
options(obj) { return this._next({ setting: { ...this._config.setting, options: obj } }); }
|
|
411
401
|
setting(func) {
|
|
412
|
-
return this._next({
|
|
413
|
-
|
|
414
|
-
|
|
402
|
+
return this._next({ setting: this._pipsub(this._config.setting, func, VigorFetchSettings) });
|
|
403
|
+
}
|
|
404
|
+
interceptors(func) {
|
|
405
|
+
return this._next({ interceptors: this._pipsub(this._config.interceptors, func, VigorFetchInterceptors) });
|
|
415
406
|
}
|
|
416
407
|
retryConfig(func) {
|
|
417
|
-
return this._next({
|
|
418
|
-
retryConfig: this._pipeSub(this._config.retryConfig, VigorRetry, func, "retryConfig")
|
|
419
|
-
});
|
|
408
|
+
return this._next({ retryConfig: this._pipsub(this._config.retryConfig, func, VigorRetry) });
|
|
420
409
|
}
|
|
421
410
|
parseConfig(func) {
|
|
422
|
-
return this._next({
|
|
423
|
-
parseConfig: this._pipeSub(this._config.parseConfig, VigorParse, func, "parseConfig")
|
|
424
|
-
});
|
|
411
|
+
return this._next({ parseConfig: this._pipsub(this._config.parseConfig, func, VigorParse) });
|
|
425
412
|
}
|
|
426
413
|
buildUrl(origin, path, query) {
|
|
427
414
|
if (!origin)
|
|
428
|
-
throw new VigorFetchError("
|
|
429
|
-
|
|
430
|
-
|
|
415
|
+
throw new VigorFetchError("INVALID_URL", {
|
|
416
|
+
method: "buildUrl",
|
|
417
|
+
data: {
|
|
418
|
+
received: origin
|
|
431
419
|
}
|
|
432
420
|
});
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
421
|
+
const url = new URL(origin);
|
|
422
|
+
const segments = [
|
|
423
|
+
url.pathname,
|
|
424
|
+
...path
|
|
425
|
+
]
|
|
426
|
+
.flat()
|
|
427
|
+
.filter(Boolean)
|
|
428
|
+
.flatMap(p => p.split('/'))
|
|
429
|
+
.filter(Boolean);
|
|
430
|
+
url.pathname = '/' + segments.join('/');
|
|
431
|
+
const params = new URLSearchParams(url.search);
|
|
432
|
+
for (const [k, v] of Object.entries(query ?? {})) {
|
|
433
|
+
if (v == null)
|
|
434
|
+
continue;
|
|
435
|
+
if (Array.isArray(v)) {
|
|
436
|
+
v.forEach(i => params.append(k, String(i)));
|
|
444
437
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (value === null || value === undefined)
|
|
448
|
-
return;
|
|
449
|
-
if (Array.isArray(value)) {
|
|
450
|
-
value.forEach(v => url.searchParams.append(key, String(v)));
|
|
451
|
-
}
|
|
452
|
-
else {
|
|
453
|
-
url.searchParams.set(key, String(value));
|
|
454
|
-
}
|
|
455
|
-
});
|
|
438
|
+
else {
|
|
439
|
+
params.set(k, String(v));
|
|
456
440
|
}
|
|
457
|
-
return url.toString();
|
|
458
|
-
}
|
|
459
|
-
catch (e) {
|
|
460
|
-
throw new VigorFetchError(`Invalid URL origin: ${origin}`, {
|
|
461
|
-
type: "invalid_url", method: "buildUrl", data: { error: e }
|
|
462
|
-
});
|
|
463
441
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
return this._next({
|
|
467
|
-
interceptors: this._pipeSub(this._config.interceptors, VigorFetchInterceptors, func, "interceptors")
|
|
468
|
-
});
|
|
442
|
+
url.search = params.toString();
|
|
443
|
+
return url.toString();
|
|
469
444
|
}
|
|
470
445
|
async request() {
|
|
471
446
|
const config = this._config;
|
|
@@ -490,15 +465,21 @@ class VigorFetch extends VigorStatus {
|
|
|
490
465
|
onError: [...config.interceptors.onError],
|
|
491
466
|
result: [...config.interceptors.result]
|
|
492
467
|
},
|
|
493
|
-
runtime: {
|
|
468
|
+
runtime: {
|
|
469
|
+
result: EMPTY
|
|
470
|
+
}
|
|
494
471
|
};
|
|
495
472
|
const throwError = (error) => { throw error; };
|
|
496
473
|
try {
|
|
497
474
|
ctx.runtime.unretrySet = new Set(ctx.setting.unretry);
|
|
498
475
|
if (!/^(https?|data|blob|file|about):\/\//.test(ctx.setting.origin))
|
|
499
|
-
throw new VigorFetchError(
|
|
500
|
-
|
|
501
|
-
|
|
476
|
+
throw new VigorFetchError("INVALID_PROTOCOL", {
|
|
477
|
+
method: "request",
|
|
478
|
+
data: {
|
|
479
|
+
expected: ["http", "https", "data", "blob", "file", "about"],
|
|
480
|
+
received: ctx.setting.origin
|
|
481
|
+
}
|
|
482
|
+
});
|
|
502
483
|
ctx.runtime.url = this.buildUrl(config.setting.origin, config.setting.path, config.setting.query);
|
|
503
484
|
const isJson = Array.isArray(ctx.setting.body) || (!!ctx.setting.body && Object.getPrototypeOf(ctx.setting.body) === Object.prototype);
|
|
504
485
|
ctx.runtime.baseOptions = {
|
|
@@ -519,9 +500,14 @@ class VigorFetch extends VigorStatus {
|
|
|
519
500
|
const checkOk = async (ctx2, { throwError }) => {
|
|
520
501
|
const result = ctx2.runtime.result;
|
|
521
502
|
if (!result.ok)
|
|
522
|
-
return throwError?.(new VigorFetchError(
|
|
523
|
-
method: "request",
|
|
524
|
-
|
|
503
|
+
return throwError?.(new VigorFetchError("FETCH_ERROR", {
|
|
504
|
+
method: "request",
|
|
505
|
+
type: "fetch_error",
|
|
506
|
+
data: {
|
|
507
|
+
status: result.status,
|
|
508
|
+
statusText: result.statusText,
|
|
509
|
+
url: result.url
|
|
510
|
+
}
|
|
525
511
|
}));
|
|
526
512
|
};
|
|
527
513
|
const handleBlacklist = (ctx2, { cancelRetry }) => {
|
|
@@ -567,96 +553,95 @@ class VigorFetch extends VigorStatus {
|
|
|
567
553
|
for (const func of ctx.interceptors.onError) {
|
|
568
554
|
await func(ctx, { setResult, throwError });
|
|
569
555
|
}
|
|
570
|
-
if (overrided && ctx.runtime.result !==
|
|
556
|
+
if (overrided && ctx.runtime.result !== EMPTY)
|
|
571
557
|
return ctx.runtime.result;
|
|
572
|
-
if (ctx.setting.default !==
|
|
558
|
+
if (ctx.setting.default !== EMPTY)
|
|
573
559
|
return ctx.setting.default;
|
|
574
560
|
throw error;
|
|
575
561
|
}
|
|
576
562
|
}
|
|
577
563
|
}
|
|
578
564
|
class VigorAllSettings extends VigorStatus {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const base = {
|
|
565
|
+
constructor(config = {}) {
|
|
566
|
+
super(config, {
|
|
582
567
|
concurrency: 5,
|
|
583
|
-
jitter: 1000
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
this._base = base;
|
|
568
|
+
jitter: 1000,
|
|
569
|
+
onlySuccess: false
|
|
570
|
+
});
|
|
587
571
|
}
|
|
588
|
-
getBase() { return this._base; }
|
|
589
572
|
concurrency(num) { return this._next({ concurrency: num }); }
|
|
590
573
|
jitter(num) { return this._next({ jitter: num }); }
|
|
574
|
+
onlySuccess(bool) { return this._next({ onlySuccess: bool }); }
|
|
591
575
|
}
|
|
592
576
|
class VigorAllInterceptors extends VigorStatus {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
const base = {
|
|
577
|
+
constructor(config = {}) {
|
|
578
|
+
super(config, {
|
|
596
579
|
before: [],
|
|
597
580
|
after: [],
|
|
598
581
|
onError: [],
|
|
599
582
|
result: []
|
|
600
|
-
};
|
|
601
|
-
super({ ...base, ...config }, (c) => new VigorAllInterceptors(c));
|
|
602
|
-
this._base = base;
|
|
583
|
+
});
|
|
603
584
|
}
|
|
604
|
-
getBase() { return this._base; }
|
|
605
585
|
before(...funcs) { return this._next({ before: [...this.getConfig().before, ...funcs.flat()] }); }
|
|
606
586
|
after(...funcs) { return this._next({ after: [...this.getConfig().after, ...funcs.flat()] }); }
|
|
607
587
|
onError(...funcs) { return this._next({ onError: [...this.getConfig().onError, ...funcs.flat()] }); }
|
|
608
588
|
result(...funcs) { return this._next({ result: [...this.getConfig().result, ...funcs.flat()] }); }
|
|
609
589
|
}
|
|
610
590
|
class VigorAll extends VigorStatus {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
const base = {
|
|
591
|
+
constructor(config = {}) {
|
|
592
|
+
super(config, {
|
|
614
593
|
target: [],
|
|
615
594
|
setting: new VigorAllSettings().getBase(),
|
|
616
595
|
interceptors: new VigorAllInterceptors().getBase()
|
|
617
|
-
};
|
|
618
|
-
|
|
619
|
-
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
_transfer(config) {
|
|
599
|
+
return new VigorAll({
|
|
600
|
+
...this._config,
|
|
601
|
+
...config
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
target(...funcs) {
|
|
605
|
+
return this._transfer({
|
|
606
|
+
target: funcs
|
|
607
|
+
});
|
|
620
608
|
}
|
|
621
|
-
getBase() { return this._base; }
|
|
622
|
-
target(...funcs) { return this._next({ target: [...this._config.target, ...funcs.flat()] }); }
|
|
623
609
|
setting(func) {
|
|
624
610
|
return this._next({
|
|
625
|
-
setting: this.
|
|
611
|
+
setting: this._pipsub(this._config.setting, func, VigorAllSettings)
|
|
626
612
|
});
|
|
627
613
|
}
|
|
628
614
|
interceptors(func) {
|
|
629
615
|
return this._next({
|
|
630
|
-
interceptors: this.
|
|
616
|
+
interceptors: this._pipsub(this._config.interceptors, func, VigorAllInterceptors)
|
|
631
617
|
});
|
|
632
618
|
}
|
|
633
619
|
async request() {
|
|
634
620
|
const config = this._config;
|
|
635
621
|
let ctx = {
|
|
636
|
-
target: [...config.target],
|
|
637
622
|
setting: { ...config.setting },
|
|
623
|
+
target: [...config.target],
|
|
638
624
|
interceptors: {
|
|
639
625
|
before: [...config.interceptors.before],
|
|
640
626
|
after: [...config.interceptors.after],
|
|
641
627
|
onError: [...config.interceptors.onError],
|
|
642
|
-
result: [...config.interceptors.result]
|
|
628
|
+
result: [...config.interceptors.result],
|
|
643
629
|
},
|
|
644
630
|
runtime: {
|
|
645
631
|
tasks: [],
|
|
646
|
-
result: []
|
|
632
|
+
result: [],
|
|
647
633
|
}
|
|
648
634
|
};
|
|
649
|
-
if (ctx.target
|
|
650
|
-
throw new
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
635
|
+
if (ctx.target.length == 0)
|
|
636
|
+
throw new VigorAllError("TARGET_MISSING", {
|
|
637
|
+
method: "request",
|
|
638
|
+
data: undefined
|
|
654
639
|
});
|
|
655
640
|
let active = 0;
|
|
656
641
|
const queue = [];
|
|
657
642
|
const runTask = async (task) => {
|
|
658
643
|
await new Promise(resolve => {
|
|
659
|
-
if (active <
|
|
644
|
+
if (active < config.setting.concurrency) {
|
|
660
645
|
active++;
|
|
661
646
|
resolve();
|
|
662
647
|
}
|
|
@@ -668,29 +653,42 @@ class VigorAll extends VigorStatus {
|
|
|
668
653
|
}
|
|
669
654
|
});
|
|
670
655
|
const throwError = (error) => { throw error; };
|
|
656
|
+
let ctxTask = {
|
|
657
|
+
target: task,
|
|
658
|
+
runtime: {
|
|
659
|
+
result: EMPTY,
|
|
660
|
+
error: null,
|
|
661
|
+
jitter: VigorRetryBackoff.randomJitter(config.setting.jitter)
|
|
662
|
+
}
|
|
663
|
+
};
|
|
671
664
|
try {
|
|
672
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
await func(ctx, { throwError });
|
|
665
|
+
await new Promise(resolve => setTimeout(resolve, ctxTask.runtime.jitter));
|
|
666
|
+
for (const func of config.interceptors.before) {
|
|
667
|
+
await func(ctxTask, { throwError });
|
|
676
668
|
}
|
|
677
|
-
|
|
678
|
-
const setResult = (result) =>
|
|
679
|
-
for (const func of
|
|
680
|
-
await func(
|
|
669
|
+
ctxTask.runtime.result = await task(ctxTask, {});
|
|
670
|
+
const setResult = (result) => ctxTask.runtime.result = result;
|
|
671
|
+
for (const func of config.interceptors.after) {
|
|
672
|
+
await func(ctxTask, { setResult, throwError });
|
|
681
673
|
}
|
|
682
|
-
|
|
674
|
+
if (ctxTask.runtime.result === EMPTY) {
|
|
675
|
+
throw new Error("Result not set");
|
|
676
|
+
}
|
|
677
|
+
return ctxTask.runtime.result;
|
|
683
678
|
}
|
|
684
679
|
catch (error) {
|
|
685
|
-
|
|
680
|
+
ctxTask.runtime.error = error;
|
|
686
681
|
let overrided = false;
|
|
687
|
-
const setResult = (result) => {
|
|
688
|
-
|
|
689
|
-
|
|
682
|
+
const setResult = (result) => {
|
|
683
|
+
overrided = true;
|
|
684
|
+
return (ctxTask.runtime.result = result);
|
|
685
|
+
};
|
|
686
|
+
for (const func of config.interceptors.onError) {
|
|
687
|
+
await func(ctxTask, { setResult, throwError });
|
|
690
688
|
}
|
|
691
|
-
if (overrided &&
|
|
692
|
-
return
|
|
693
|
-
throw error;
|
|
689
|
+
if (overrided && ctxTask.runtime.result !== EMPTY)
|
|
690
|
+
return ctxTask.runtime.result;
|
|
691
|
+
throw ctxTask.runtime.error;
|
|
694
692
|
}
|
|
695
693
|
finally {
|
|
696
694
|
active--;
|
|
@@ -701,26 +699,28 @@ class VigorAll extends VigorStatus {
|
|
|
701
699
|
};
|
|
702
700
|
ctx.runtime.tasks = ctx.target.map(task => runTask(task));
|
|
703
701
|
const settled = await Promise.allSettled(ctx.runtime.tasks);
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
702
|
+
const isFailed = Symbol("FAILED");
|
|
703
|
+
ctx.runtime.result = settled.map((res, idx) => {
|
|
704
|
+
if (res.status === "fulfilled")
|
|
705
|
+
return res.value;
|
|
706
|
+
if (ctx.setting.onlySuccess)
|
|
707
|
+
return isFailed;
|
|
708
|
+
return new VigorAllError("REQUEST_FAILED", {
|
|
709
|
+
method: "request",
|
|
710
|
+
data: {
|
|
711
|
+
index: idx,
|
|
712
|
+
error: res.reason
|
|
710
713
|
}
|
|
711
714
|
});
|
|
712
|
-
});
|
|
715
|
+
}).filter(i => i !== isFailed);
|
|
713
716
|
const setResult = (result) => ctx.runtime.result = result;
|
|
714
717
|
const throwError = (error) => { throw error; };
|
|
715
|
-
for (const func of
|
|
718
|
+
for (const func of config.interceptors.result) {
|
|
716
719
|
await func(ctx, { setResult, throwError });
|
|
717
720
|
}
|
|
718
721
|
return ctx.runtime.result;
|
|
719
722
|
}
|
|
720
723
|
}
|
|
721
|
-
function calculateJitter(jitter) {
|
|
722
|
-
return jitter * (Math.random() * 2 - 1);
|
|
723
|
-
}
|
|
724
724
|
class Vigor {
|
|
725
725
|
registry;
|
|
726
726
|
constructor(config) {
|
|
@@ -754,8 +754,11 @@ class Vigor {
|
|
|
754
754
|
fetch(origin) {
|
|
755
755
|
return this.registry.VigorFetch.main().origin(origin);
|
|
756
756
|
}
|
|
757
|
-
all(
|
|
758
|
-
|
|
757
|
+
all(...args) {
|
|
758
|
+
const flatTasks = args.flat();
|
|
759
|
+
return this.registry.VigorAll
|
|
760
|
+
.main()
|
|
761
|
+
.target(...flatTasks);
|
|
759
762
|
}
|
|
760
763
|
parse(response) {
|
|
761
764
|
return this.registry.VigorParse.main().target(response);
|