vigor-fetch 2.2.8 → 3.0.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 +958 -264
- package/dist/index.d.ts +453 -368
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +988 -631
- package/dist/index.mjs +988 -616
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,43 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
5
|
+
const VigorErrorMessageFuncs = {
|
|
6
|
+
INVALID_TARGET: ({ expected, received }) => `Invalid Task: ${typeof received} (expected: ${expected.join(', ')})`,
|
|
7
7
|
EXHAUSTED: ({ maxAttempts }) => `Retry exhausted: max ${maxAttempts})`,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
UNKNOWN: () => `Unknown error`
|
|
17
|
-
};
|
|
18
|
-
const normalizeError = (obj) => {
|
|
19
|
-
if (obj instanceof Error) {
|
|
20
|
-
throw obj;
|
|
21
|
-
}
|
|
22
|
-
throw new Error(String(obj));
|
|
8
|
+
TIMED_OUT: ({ limit, attempt }) => `Timeout: exceeded ${limit}ms (attempt: ${attempt})`,
|
|
9
|
+
INVALID_CONTENT_TYPE: ({ expected, received, response }) => `Invalid Content Type Header: ${typeof received} (expected: ${expected.join(', ')})`,
|
|
10
|
+
PARSER_NOT_FOUND: ({ expected, received, response }) => `Parser Not Found For Header: ${typeof received} (expected: ${expected.join(', ')})`,
|
|
11
|
+
PARSER_ALL_FAILED: ({ tried, response }) => `All Parser Failed, Tried: ${tried.join(', ')}`,
|
|
12
|
+
INVALID_PROTOCOL: ({ expected, received }) => `Invalid Protocol: ${typeof received} (expected: ${expected.join(', ')})`,
|
|
13
|
+
INVALID_BODY: ({ expected, received }) => `Invalid Body: ${typeof received} (expected: ${expected.join(', ')})`,
|
|
14
|
+
FETCH_FAILED: ({ status, response, url, headers, body, statusText }) => `Fetch Failed: ${status}`,
|
|
15
|
+
EMPTY_TARGET: ({}) => `Empty Body`
|
|
23
16
|
};
|
|
24
17
|
class VigorError extends Error {
|
|
25
18
|
timestamp = new Date();
|
|
26
|
-
method;
|
|
27
|
-
code;
|
|
28
19
|
cause;
|
|
29
|
-
|
|
30
|
-
type;
|
|
20
|
+
code;
|
|
31
21
|
data;
|
|
22
|
+
method;
|
|
23
|
+
stats;
|
|
24
|
+
context;
|
|
32
25
|
constructor(code, options) {
|
|
33
|
-
const messageFn =
|
|
26
|
+
const messageFn = VigorErrorMessageFuncs[code];
|
|
34
27
|
const message = `[${code}] ${messageFn(options?.data)}`;
|
|
35
28
|
super(message, { cause: options?.cause });
|
|
36
29
|
this.name = new.target.name;
|
|
37
|
-
this.method = options?.method;
|
|
38
30
|
this.code = code;
|
|
39
|
-
this.
|
|
40
|
-
this.type = options?.type;
|
|
31
|
+
this.cause = options.cause;
|
|
41
32
|
this.data = options.data;
|
|
33
|
+
this.method = options.method;
|
|
34
|
+
this.stats = options.stats;
|
|
35
|
+
this.context = options.context;
|
|
42
36
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
43
37
|
Error.captureStackTrace?.(this, new.target);
|
|
44
38
|
}
|
|
@@ -63,760 +57,1123 @@ class VigorAllError extends VigorError {
|
|
|
63
57
|
super(code, options);
|
|
64
58
|
}
|
|
65
59
|
}
|
|
66
|
-
const EMPTY = Symbol("EMPTY");
|
|
67
60
|
class VigorStatus {
|
|
68
61
|
_base;
|
|
62
|
+
ctor;
|
|
69
63
|
_config;
|
|
70
|
-
constructor(config, _base) {
|
|
64
|
+
constructor(config = {}, _base, ctor) {
|
|
71
65
|
this._base = _base;
|
|
72
|
-
this.
|
|
66
|
+
this.ctor = ctor;
|
|
67
|
+
this._config = { ..._base, ...config };
|
|
73
68
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
_mergeConfig(source, target) {
|
|
70
|
+
const isPlainObject = (val) => val !== null && typeof val === 'object' && Object.getPrototypeOf(val) === Object.prototype;
|
|
71
|
+
if (target === undefined || target === null) {
|
|
72
|
+
return source;
|
|
73
|
+
}
|
|
74
|
+
if (isPlainObject(source) && isPlainObject(target)) {
|
|
75
|
+
const result = { ...source };
|
|
76
|
+
Object.keys(target).forEach((key) => {
|
|
77
|
+
result[key] = this._mergeConfig(result[key], target[key]);
|
|
78
|
+
});
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
if (Array.isArray(source) && Array.isArray(target)) {
|
|
82
|
+
return [...source, ...target];
|
|
83
|
+
}
|
|
84
|
+
return target;
|
|
79
85
|
}
|
|
86
|
+
_next(config) { return this.ctor(this._mergeConfig(this._config, config)); }
|
|
87
|
+
_getConfig() { return this._config; }
|
|
88
|
+
_getBase() { return this._base; }
|
|
80
89
|
}
|
|
90
|
+
const VigorDefault = Symbol("DEFAULT");
|
|
81
91
|
class VigorRetrySettings extends VigorStatus {
|
|
82
|
-
constructor(config
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
default: EMPTY
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
count(num) { return this._next({ count: num }); }
|
|
91
|
-
limit(num) { return this._next({ limit: num }); }
|
|
92
|
-
maxDelay(num) { return this._next({ maxDelay: num }); }
|
|
93
|
-
default(obj) { return this._next({ default: obj }); }
|
|
94
|
-
}
|
|
95
|
-
class VigorRetryBackoff extends VigorStatus {
|
|
96
|
-
constructor(config = {}) {
|
|
97
|
-
super(config, {
|
|
98
|
-
initialDelay: 0,
|
|
99
|
-
baseDelay: 1000,
|
|
100
|
-
factor: 1.7,
|
|
92
|
+
constructor(config) {
|
|
93
|
+
const base = {
|
|
94
|
+
default: VigorDefault,
|
|
95
|
+
timeout: 20 * 1000,
|
|
96
|
+
attempt: 5,
|
|
101
97
|
jitter: 1000
|
|
102
|
-
}
|
|
98
|
+
};
|
|
99
|
+
super(config, base, (c) => new VigorRetrySettings(c));
|
|
103
100
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
default(unk) { return this._next({ default: unk }); }
|
|
102
|
+
timeout(num) { return this._next({ timeout: num }); }
|
|
103
|
+
attempt(num) { return this._next({ attempt: num }); }
|
|
107
104
|
jitter(num) { return this._next({ jitter: num }); }
|
|
108
|
-
static randomJitter(num) { return Math.random() * num; }
|
|
109
105
|
}
|
|
110
106
|
class VigorRetryInterceptors extends VigorStatus {
|
|
111
|
-
constructor(config
|
|
112
|
-
|
|
107
|
+
constructor(config) {
|
|
108
|
+
const base = {
|
|
113
109
|
before: [],
|
|
114
110
|
after: [],
|
|
115
|
-
|
|
111
|
+
result: [],
|
|
112
|
+
retryIf: [],
|
|
116
113
|
onRetry: [],
|
|
117
|
-
|
|
118
|
-
}
|
|
114
|
+
onError: []
|
|
115
|
+
};
|
|
116
|
+
super(config, base, (c) => new VigorRetryInterceptors(c));
|
|
119
117
|
}
|
|
120
|
-
before(...funcs) { return this._next({
|
|
121
|
-
after(...funcs) { return this._next({
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
before(...funcs) { return this._next({ before: funcs.flat() }); }
|
|
119
|
+
after(...funcs) { return this._next({ after: funcs.flat() }); }
|
|
120
|
+
result(...funcs) { return this._next({ result: funcs.flat() }); }
|
|
121
|
+
retryIf(...funcs) { return this._next({ retryIf: funcs.flat() }); }
|
|
122
|
+
onRetry(...funcs) { return this._next({ onRetry: funcs.flat() }); }
|
|
123
|
+
onError(...funcs) { return this._next({ onError: funcs.flat() }); }
|
|
125
124
|
}
|
|
126
|
-
class
|
|
127
|
-
constructor(config
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
interceptors: new VigorRetryInterceptors().getBase(),
|
|
133
|
-
controller: config.controller || new AbortController()
|
|
134
|
-
});
|
|
125
|
+
class VigorRetryAlgorithmsConstant extends VigorStatus {
|
|
126
|
+
constructor(config) {
|
|
127
|
+
const base = {
|
|
128
|
+
interval: 2000
|
|
129
|
+
};
|
|
130
|
+
super(config, base, (c) => new VigorRetryAlgorithmsConstant(c));
|
|
135
131
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
interval(num) { return this._next({ interval: num }); }
|
|
133
|
+
/** @internal */
|
|
134
|
+
_calculateDelay(attempt) {
|
|
135
|
+
return this._config.interval;
|
|
139
136
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
137
|
+
}
|
|
138
|
+
class VigorRetryAlgorithmsLinear extends VigorStatus {
|
|
139
|
+
constructor(config) {
|
|
140
|
+
const base = {
|
|
141
|
+
initial: 1000,
|
|
142
|
+
increment: 1000,
|
|
143
|
+
minDelay: 500,
|
|
144
|
+
maxDelay: 20 * 1000
|
|
145
|
+
};
|
|
146
|
+
super(config, base, (c) => new VigorRetryAlgorithmsLinear(c));
|
|
147
|
+
}
|
|
148
|
+
initial(num) { return this._next({ initial: num }); }
|
|
149
|
+
increment(num) { return this._next({ increment: num }); }
|
|
150
|
+
minDelay(num) { return this._next({ minDelay: num }); }
|
|
151
|
+
maxDelay(num) { return this._next({ maxDelay: num }); }
|
|
152
|
+
/** @internal */
|
|
153
|
+
_calculateDelay(attempt) {
|
|
154
|
+
const { initial, increment, minDelay, maxDelay } = this._config;
|
|
155
|
+
return Math.max(minDelay, Math.min(maxDelay, initial + increment * attempt));
|
|
144
156
|
}
|
|
145
|
-
|
|
146
|
-
|
|
157
|
+
}
|
|
158
|
+
class VigorRetryAlgorithmsBackoff extends VigorStatus {
|
|
159
|
+
constructor(config) {
|
|
160
|
+
const base = {
|
|
161
|
+
initial: 1000,
|
|
162
|
+
multiplier: 1.7,
|
|
163
|
+
unit: 1000,
|
|
164
|
+
minDelay: 500,
|
|
165
|
+
maxDelay: 20 * 1000
|
|
166
|
+
};
|
|
167
|
+
super(config, base, (c) => new VigorRetryAlgorithmsBackoff(c));
|
|
168
|
+
}
|
|
169
|
+
initial(num) { return this._next({ initial: num }); }
|
|
170
|
+
multiplier(num) { return this._next({ multiplier: num }); }
|
|
171
|
+
unit(num) { return this._next({ unit: num }); }
|
|
172
|
+
minDelay(num) { return this._next({ minDelay: num }); }
|
|
173
|
+
maxDelay(num) { return this._next({ maxDelay: num }); }
|
|
174
|
+
/** @internal */
|
|
175
|
+
_calculateDelay(attempt) {
|
|
176
|
+
const { initial, multiplier, unit, minDelay, maxDelay } = this._config;
|
|
177
|
+
return Math.max(minDelay, Math.min(maxDelay, initial + unit * Math.pow(multiplier, attempt)));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
class VigorRetryAlgorithmsCustom extends VigorStatus {
|
|
181
|
+
constructor(config) {
|
|
182
|
+
const base = {
|
|
183
|
+
func: (attempt) => attempt * 1000,
|
|
184
|
+
minDelay: 500,
|
|
185
|
+
maxDelay: 20 * 1000
|
|
186
|
+
};
|
|
187
|
+
super(config, base, (c) => new VigorRetryAlgorithmsCustom(c));
|
|
188
|
+
}
|
|
189
|
+
func(num) { return this._next({ func: num }); }
|
|
190
|
+
/** @internal */
|
|
191
|
+
_calculateDelay(attempt) {
|
|
192
|
+
const { func, minDelay, maxDelay } = this._config;
|
|
193
|
+
return Math.max(minDelay, Math.min(maxDelay, func(attempt)));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
class VigorRetry extends VigorStatus {
|
|
197
|
+
constructor(config) {
|
|
198
|
+
const base = {
|
|
199
|
+
target: VigorDefault,
|
|
200
|
+
settings: new VigorRetrySettings()._getBase(),
|
|
201
|
+
interceptors: new VigorRetryInterceptors()._getBase(),
|
|
202
|
+
algorithm: new VigorRetryAlgorithmsBackoff()._calculateDelay,
|
|
203
|
+
abortSignals: []
|
|
204
|
+
};
|
|
205
|
+
super(config, base, (c) => new VigorRetry(c));
|
|
206
|
+
}
|
|
207
|
+
RetryAlgorithms = {
|
|
208
|
+
constant: (config) => new VigorRetryAlgorithmsConstant(config),
|
|
209
|
+
linear: (config) => new VigorRetryAlgorithmsLinear(config),
|
|
210
|
+
backoff: (config) => new VigorRetryAlgorithmsBackoff(config),
|
|
211
|
+
custom: (config) => new VigorRetryAlgorithmsCustom(config)
|
|
212
|
+
};
|
|
213
|
+
target(func) { return this._next({ target: func }); }
|
|
214
|
+
settings(func) {
|
|
215
|
+
if (typeof func === 'function') {
|
|
216
|
+
return this._next({ settings: func(new VigorRetrySettings(this._config.settings))._getConfig() });
|
|
217
|
+
}
|
|
218
|
+
return this._next({ settings: func });
|
|
147
219
|
}
|
|
148
220
|
interceptors(func) {
|
|
149
|
-
|
|
221
|
+
if (typeof func === 'function') {
|
|
222
|
+
return this._next({ interceptors: func(new VigorRetryInterceptors(this._config.interceptors))._getConfig() });
|
|
223
|
+
}
|
|
224
|
+
return this._next({ interceptors: func });
|
|
225
|
+
}
|
|
226
|
+
algorithms(func) {
|
|
227
|
+
const instance = func(this.RetryAlgorithms);
|
|
228
|
+
return this._next({ algorithm: (attempt) => instance._calculateDelay(attempt) });
|
|
150
229
|
}
|
|
151
|
-
|
|
152
|
-
|
|
230
|
+
abortSignals(...abortSignals) {
|
|
231
|
+
return this._next({ abortSignals: abortSignals.flat() });
|
|
232
|
+
}
|
|
233
|
+
async request(config, timeline = []) {
|
|
234
|
+
const stats = this._mergeConfig(this._config, config);
|
|
153
235
|
let ctx = {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
runtime: {
|
|
166
|
-
result: EMPTY,
|
|
167
|
-
controller: null,
|
|
168
|
-
attempt: 0,
|
|
169
|
-
aborted: false,
|
|
170
|
-
signal: null,
|
|
171
|
-
delay: 0,
|
|
172
|
-
retry: false,
|
|
173
|
-
}
|
|
236
|
+
result: VigorDefault,
|
|
237
|
+
error: VigorDefault,
|
|
238
|
+
attempt: 0,
|
|
239
|
+
delay: 0,
|
|
240
|
+
controller: VigorDefault,
|
|
241
|
+
timeline: timeline,
|
|
242
|
+
stats,
|
|
243
|
+
};
|
|
244
|
+
const throwError = (error) => {
|
|
245
|
+
ctx.timeline.push({ action: "throwError called", content: error });
|
|
246
|
+
throw error;
|
|
174
247
|
};
|
|
175
|
-
const throwError = (error) => { throw error; };
|
|
176
248
|
try {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
249
|
+
if (typeof stats.target !== 'function')
|
|
250
|
+
throw new VigorRetryError("INVALID_TARGET", {
|
|
251
|
+
method: "request",
|
|
252
|
+
data: {
|
|
253
|
+
expected: ["function"],
|
|
254
|
+
received: stats.target
|
|
255
|
+
},
|
|
256
|
+
stats: stats,
|
|
257
|
+
context: ctx
|
|
258
|
+
});
|
|
259
|
+
while (ctx.attempt < stats.settings.attempt) {
|
|
260
|
+
ctx.attempt++;
|
|
261
|
+
ctx.timeline.push({ action: "increased attempt", content: ctx.attempt });
|
|
262
|
+
let broke = false;
|
|
263
|
+
const breakRetry = (error) => {
|
|
264
|
+
ctx.timeline.push({ action: "breakRetry called", content: error });
|
|
265
|
+
broke = true;
|
|
266
|
+
throw error;
|
|
267
|
+
};
|
|
185
268
|
try {
|
|
186
|
-
ctx.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
]);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
ctx.runtime.aborted = true;
|
|
195
|
-
reject(ctx.runtime.signal.reason);
|
|
196
|
-
};
|
|
197
|
-
ctx.runtime.signal.addEventListener("abort", listener, { once: true });
|
|
198
|
-
timerId = setTimeout(() => {
|
|
199
|
-
if (ctx.runtime.aborted)
|
|
200
|
-
return;
|
|
201
|
-
abort(new VigorRetryError("TIMEOUT", {
|
|
202
|
-
method: "request",
|
|
203
|
-
type: "timeout",
|
|
204
|
-
data: {
|
|
205
|
-
limit: ctx.setting.limit,
|
|
206
|
-
attempt: ctx.runtime.attempt
|
|
207
|
-
}
|
|
208
|
-
}));
|
|
209
|
-
}, ctx.setting.limit);
|
|
210
|
-
});
|
|
211
|
-
for (const func of ctx.interceptors.before) {
|
|
212
|
-
await func(ctx, { setAttempt, throwError, abort });
|
|
213
|
-
if (ctx.runtime.signal.aborted)
|
|
214
|
-
normalizeError(ctx.runtime.signal.reason);
|
|
269
|
+
ctx.timeline.push({ action: "process request_once handling", content: ctx.attempt });
|
|
270
|
+
const controller = new AbortController();
|
|
271
|
+
const timeoutController = new AbortController();
|
|
272
|
+
const signal = AbortSignal.any([controller.signal, timeoutController.signal, ...stats.abortSignals]);
|
|
273
|
+
const abort = (err) => controller.abort(err);
|
|
274
|
+
ctx.timeline.push({ action: "interceptor handling: before", content: stats.interceptors.before });
|
|
275
|
+
for (const func of stats.interceptors.before) {
|
|
276
|
+
await func(ctx, { throwError, breakRetry, abort });
|
|
215
277
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
278
|
+
const timeoutTimer = setTimeout(() => {
|
|
279
|
+
clearTimeout(timeoutTimer);
|
|
280
|
+
timeoutController.abort(new VigorRetryError("TIMED_OUT", {
|
|
281
|
+
method: "request",
|
|
282
|
+
data: {
|
|
283
|
+
limit: stats.settings.timeout,
|
|
284
|
+
attempt: ctx.attempt
|
|
285
|
+
},
|
|
286
|
+
}));
|
|
287
|
+
}, stats.settings.timeout);
|
|
288
|
+
signal.throwIfAborted();
|
|
289
|
+
let onAbort;
|
|
290
|
+
try {
|
|
291
|
+
ctx.result = await Promise.race([
|
|
292
|
+
stats.target(ctx, { abort, signal }),
|
|
293
|
+
new Promise((_, rej) => {
|
|
294
|
+
onAbort = () => rej(signal.reason);
|
|
295
|
+
signal.addEventListener("abort", onAbort);
|
|
296
|
+
})
|
|
297
|
+
]);
|
|
225
298
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
normalizeError(ctx.runtime.signal.reason);
|
|
231
|
-
ctx.runtime.retry = true;
|
|
232
|
-
ctx.runtime.error = error;
|
|
233
|
-
const proceedRetry = () => ctx.runtime.retry = true;
|
|
234
|
-
const cancelRetry = (error) => { ctx.runtime.error = error; return (ctx.runtime.retry = false); };
|
|
235
|
-
for (const func of ctx.interceptors.retryIf) {
|
|
236
|
-
await func(ctx, { throwError, proceedRetry, cancelRetry });
|
|
299
|
+
finally {
|
|
300
|
+
clearTimeout(timeoutTimer);
|
|
301
|
+
if (onAbort)
|
|
302
|
+
signal.removeEventListener("abort", onAbort);
|
|
237
303
|
}
|
|
238
|
-
|
|
239
|
-
|
|
304
|
+
const setResult = (unk) => {
|
|
305
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
306
|
+
ctx.result = unk;
|
|
307
|
+
return unk;
|
|
308
|
+
};
|
|
309
|
+
ctx.timeline.push({ action: "interceptor handling: after", content: stats.interceptors.after });
|
|
310
|
+
for (const func of stats.interceptors.after) {
|
|
311
|
+
await func(ctx, { setResult, throwError, breakRetry });
|
|
240
312
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
for (const func of ctx.interceptors.onRetry) {
|
|
245
|
-
await func(ctx, { setAttempt, throwError, setDelay });
|
|
313
|
+
ctx.timeline.push({ action: "interceptor handling: result", content: stats.interceptors.result });
|
|
314
|
+
for (const func of stats.interceptors.result) {
|
|
315
|
+
await func(ctx, { setResult, throwError });
|
|
246
316
|
}
|
|
247
|
-
|
|
248
|
-
const timer = setTimeout(resolve, ctx.runtime.delay);
|
|
249
|
-
const abortHandler = () => {
|
|
250
|
-
clearTimeout(timer);
|
|
251
|
-
reject(ctx.controller.signal.reason);
|
|
252
|
-
};
|
|
253
|
-
if (ctx.controller.signal.aborted)
|
|
254
|
-
return abortHandler();
|
|
255
|
-
ctx.controller.signal.addEventListener("abort", abortHandler, { once: true });
|
|
256
|
-
});
|
|
317
|
+
return ctx.result;
|
|
257
318
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
319
|
+
catch (error) {
|
|
320
|
+
ctx.error = error;
|
|
321
|
+
ctx.timeline.push({ action: "process error_once handling", content: error });
|
|
322
|
+
if (broke)
|
|
323
|
+
throw error;
|
|
324
|
+
let proceed = true;
|
|
325
|
+
const proceedRetry = () => {
|
|
326
|
+
ctx.timeline.push({ action: "proceedRetry called", content: true });
|
|
327
|
+
return proceed = true;
|
|
328
|
+
};
|
|
329
|
+
const cancelRetry = () => {
|
|
330
|
+
ctx.timeline.push({ action: "cancelRetry called", content: false });
|
|
331
|
+
return proceed = false;
|
|
332
|
+
};
|
|
333
|
+
ctx.timeline.push({ action: "interceptor handling: retryIf", content: stats.interceptors.result });
|
|
334
|
+
for (const func of stats.interceptors.retryIf) {
|
|
335
|
+
await func(ctx, { proceedRetry, cancelRetry });
|
|
336
|
+
}
|
|
337
|
+
if (!proceed)
|
|
338
|
+
throw error;
|
|
339
|
+
ctx.delay = VigorDefault;
|
|
340
|
+
const setDelay = (num) => {
|
|
341
|
+
ctx.timeline.push({ action: "setDelay called", content: num });
|
|
342
|
+
return ctx.delay = num;
|
|
343
|
+
};
|
|
344
|
+
const setAttempt = (num) => {
|
|
345
|
+
ctx.timeline.push({ action: "setAttempt called", content: num });
|
|
346
|
+
return ctx.attempt = num;
|
|
347
|
+
};
|
|
348
|
+
ctx.timeline.push({ action: "interceptor handling: onRetry", content: stats.interceptors.onRetry });
|
|
349
|
+
for (const func of stats.interceptors.onRetry) {
|
|
350
|
+
await func(ctx, { throwError, setDelay, setAttempt });
|
|
351
|
+
}
|
|
352
|
+
if (typeof ctx.delay !== 'number')
|
|
353
|
+
ctx.delay = stats.algorithm(ctx.attempt) + Math.random() * stats.settings.jitter;
|
|
354
|
+
const delay = ctx.delay;
|
|
355
|
+
await new Promise(r => setTimeout(r, delay));
|
|
262
356
|
}
|
|
263
|
-
ctx.runtime.attempt++;
|
|
264
357
|
}
|
|
265
358
|
throw new VigorRetryError("EXHAUSTED", {
|
|
266
359
|
method: "request",
|
|
267
|
-
type: "retry",
|
|
268
360
|
data: {
|
|
269
|
-
maxAttempts:
|
|
270
|
-
}
|
|
361
|
+
maxAttempts: stats.settings.attempt,
|
|
362
|
+
},
|
|
363
|
+
context: ctx
|
|
271
364
|
});
|
|
272
365
|
}
|
|
273
366
|
catch (error) {
|
|
274
|
-
ctx.
|
|
275
|
-
let
|
|
276
|
-
const setResult = (
|
|
277
|
-
|
|
278
|
-
|
|
367
|
+
ctx.error = error;
|
|
368
|
+
let overwritten = false;
|
|
369
|
+
const setResult = (unk) => {
|
|
370
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
371
|
+
ctx.result = unk;
|
|
372
|
+
overwritten = true;
|
|
373
|
+
return unk;
|
|
374
|
+
};
|
|
375
|
+
let restarted = false;
|
|
376
|
+
const restart = () => {
|
|
377
|
+
ctx.timeline.push({ action: "restart called" });
|
|
378
|
+
restarted = true;
|
|
379
|
+
};
|
|
380
|
+
ctx.timeline.push({ action: "interceptor handling: onError", content: stats.interceptors.onError });
|
|
381
|
+
for (const func of stats.interceptors.onError) {
|
|
382
|
+
await func(ctx, { setResult, throwError, restart });
|
|
383
|
+
}
|
|
384
|
+
if (restarted) {
|
|
385
|
+
return await this.request(stats, ctx.timeline);
|
|
279
386
|
}
|
|
280
|
-
if (
|
|
281
|
-
return ctx.
|
|
282
|
-
if (
|
|
283
|
-
return
|
|
387
|
+
if (overwritten)
|
|
388
|
+
return ctx.result;
|
|
389
|
+
if (stats.settings.default !== VigorDefault)
|
|
390
|
+
return stats.settings.default;
|
|
284
391
|
throw error;
|
|
285
392
|
}
|
|
286
393
|
}
|
|
287
394
|
}
|
|
288
|
-
class
|
|
289
|
-
constructor(config
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
395
|
+
class VigorParseSettings extends VigorStatus {
|
|
396
|
+
constructor(config) {
|
|
397
|
+
const base = {
|
|
398
|
+
raw: false,
|
|
399
|
+
default: VigorDefault
|
|
400
|
+
};
|
|
401
|
+
super(config, base, (c) => new VigorParseSettings(c));
|
|
293
402
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
403
|
+
original(bool) { return this._next({ raw: bool }); }
|
|
404
|
+
default(unk) { return this._next({ default: unk }); }
|
|
405
|
+
}
|
|
406
|
+
class VigorParseStrategies extends VigorStatus {
|
|
407
|
+
constructor(config) {
|
|
408
|
+
const base = {
|
|
409
|
+
funcs: []
|
|
410
|
+
};
|
|
411
|
+
super(config, base, (c) => new VigorParseStrategies(c));
|
|
412
|
+
this._config.funcs.push(this.ParseAutoAlgorithms.contentType);
|
|
413
|
+
}
|
|
414
|
+
ParseAutoHeaders = [
|
|
415
|
+
{ header: "application/json", regExp: /application\/(.+\+)?json(.+\+)?/i, method: (res) => res.json() },
|
|
416
|
+
{ header: "application/xml", regExp: /application\/(.+\+)?xml(.+\+)?/i, method: (res) => res.text() },
|
|
417
|
+
{ header: "application/x-www-form-urlencoded", regExp: /application\/(.+\+)?x-www-form-urlencoded(.+\+)?/i, method: (res) => res.formData() },
|
|
418
|
+
{ header: "application/octet-stream", regExp: /application\/(.+\+)?octet-stream(.+\+)?/i, method: (res) => res.arrayBuffer() },
|
|
419
|
+
{ header: "image/*", regExp: /^image\/.+/i, method: (res) => res.blob() },
|
|
420
|
+
{ header: "audio/*", regExp: /^audio\/.+/i, method: (res) => res.blob() },
|
|
421
|
+
{ header: "video/*", regExp: /^video\/.+/i, method: (res) => res.blob() },
|
|
422
|
+
{ header: "multipart/form-data", regExp: /multipart\/(.+\+)?form-data(.+\+)?/i, method: (res) => res.formData() },
|
|
423
|
+
{ header: "text/*", regExp: /^text\/.+/i, method: (res) => res.text() },
|
|
300
424
|
];
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
425
|
+
ParseAutoMethods = [
|
|
426
|
+
{ title: "json", method: (res) => res.json() },
|
|
427
|
+
{ title: "formData", method: (res) => res.formData() },
|
|
428
|
+
{ title: "text", method: (res) => res.text() },
|
|
429
|
+
{ title: "blob", method: (res) => res.blob() },
|
|
430
|
+
];
|
|
431
|
+
ParseAutoAlgorithms = {
|
|
432
|
+
contentType: async (response) => {
|
|
433
|
+
const parsers = this.ParseAutoHeaders;
|
|
434
|
+
const contentTypeHeader = response.headers.get("content-type");
|
|
435
|
+
if (!contentTypeHeader)
|
|
436
|
+
throw new VigorParseError("INVALID_CONTENT_TYPE", {
|
|
437
|
+
method: "ParseAutoAlgorithms.contentType",
|
|
438
|
+
data: {
|
|
439
|
+
expected: ["string"],
|
|
440
|
+
received: contentTypeHeader,
|
|
441
|
+
response: response
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
const toDo = parsers.find(parser => parser.regExp.test(contentTypeHeader));
|
|
445
|
+
if (!toDo)
|
|
446
|
+
throw new VigorParseError("PARSER_NOT_FOUND", {
|
|
447
|
+
method: "ParseAutoAlgorithms.contentType",
|
|
448
|
+
data: {
|
|
449
|
+
expected: parsers.map(parser => parser.header),
|
|
450
|
+
received: contentTypeHeader,
|
|
451
|
+
response: response
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
return await toDo.method(response);
|
|
455
|
+
},
|
|
456
|
+
sniff: async (response) => {
|
|
457
|
+
const parsers = this.ParseAutoMethods;
|
|
458
|
+
for (const [i, parser] of parsers.entries()) {
|
|
459
|
+
const cloned = (i === parsers.length - 1)
|
|
460
|
+
? response
|
|
461
|
+
: response.clone();
|
|
462
|
+
try {
|
|
463
|
+
const data = await parser.method(cloned);
|
|
464
|
+
return data;
|
|
465
|
+
}
|
|
466
|
+
catch { }
|
|
467
|
+
}
|
|
468
|
+
throw new VigorParseError("PARSER_ALL_FAILED", {
|
|
469
|
+
method: "ParseAutoAlgorithms.sniff",
|
|
470
|
+
data: {
|
|
471
|
+
tried: parsers.map(parser => parser.title),
|
|
472
|
+
response: response
|
|
473
|
+
}
|
|
313
474
|
});
|
|
314
475
|
}
|
|
315
|
-
|
|
316
|
-
|
|
476
|
+
};
|
|
477
|
+
contentType() { return this._next({ funcs: [this.ParseAutoAlgorithms.contentType] }); }
|
|
478
|
+
sniff() { return this._next({ funcs: [this.ParseAutoAlgorithms.sniff] }); }
|
|
479
|
+
json() { return this._next({ funcs: [(res) => res.json()] }); }
|
|
480
|
+
text() { return this._next({ funcs: [(res) => res.text()] }); }
|
|
481
|
+
arrayBuffer() { return this._next({ funcs: [(res) => res.arrayBuffer()] }); }
|
|
482
|
+
blob() { return this._next({ funcs: [(res) => res.blob()] }); }
|
|
483
|
+
bytes() { return this._next({ funcs: [(res) => res.arrayBuffer().then(r => new Uint8Array(r))] }); }
|
|
484
|
+
formData() { return this._next({ funcs: [(res) => res.formData()] }); }
|
|
485
|
+
}
|
|
486
|
+
class VigorParseInterceptors extends VigorStatus {
|
|
487
|
+
constructor(config) {
|
|
488
|
+
const base = {
|
|
489
|
+
before: [],
|
|
490
|
+
after: [],
|
|
491
|
+
result: [],
|
|
492
|
+
onError: []
|
|
493
|
+
};
|
|
494
|
+
super(config, base, (c) => new VigorParseInterceptors(c));
|
|
495
|
+
}
|
|
496
|
+
before(...funcs) { return this._next({ before: funcs.flat() }); }
|
|
497
|
+
after(...funcs) { return this._next({ after: funcs.flat() }); }
|
|
498
|
+
result(...funcs) { return this._next({ result: funcs.flat() }); }
|
|
499
|
+
onError(...funcs) { return this._next({ onError: funcs.flat() }); }
|
|
500
|
+
}
|
|
501
|
+
class VigorParse extends VigorStatus {
|
|
502
|
+
constructor(config) {
|
|
503
|
+
const base = {
|
|
504
|
+
target: VigorDefault,
|
|
505
|
+
settings: new VigorParseSettings()._getBase(),
|
|
506
|
+
strategies: new VigorParseStrategies()._getBase(),
|
|
507
|
+
interceptors: new VigorParseInterceptors()._getBase()
|
|
508
|
+
};
|
|
509
|
+
super(config, base, (c) => new VigorParse(c));
|
|
510
|
+
}
|
|
511
|
+
target(response) { return this._next({ target: response }); }
|
|
512
|
+
settings(func) {
|
|
513
|
+
if (typeof func === 'function') {
|
|
514
|
+
return this._next({ settings: func(new VigorParseSettings(this._config.settings))._getConfig() });
|
|
515
|
+
}
|
|
516
|
+
return this._next({ settings: func });
|
|
517
|
+
}
|
|
518
|
+
strategies(func) {
|
|
519
|
+
if (typeof func === 'function') {
|
|
520
|
+
return this._next({ strategies: func(new VigorParseStrategies(this._config.strategies))._getConfig() });
|
|
521
|
+
}
|
|
522
|
+
return this._next({ strategies: func });
|
|
523
|
+
}
|
|
524
|
+
interceptors(func) {
|
|
525
|
+
if (typeof func === 'function') {
|
|
526
|
+
return this._next({ interceptors: func(new VigorParseInterceptors(this._config.interceptors))._getConfig() });
|
|
317
527
|
}
|
|
318
|
-
|
|
319
|
-
|
|
528
|
+
return this._next({ interceptors: func });
|
|
529
|
+
}
|
|
530
|
+
async request(config, timeline = []) {
|
|
531
|
+
const stats = this._mergeConfig(this._config, config);
|
|
532
|
+
const target = stats.target;
|
|
533
|
+
let ctx = {
|
|
534
|
+
timeline: timeline,
|
|
535
|
+
stats,
|
|
536
|
+
response: target,
|
|
537
|
+
result: VigorDefault,
|
|
538
|
+
error: VigorDefault,
|
|
539
|
+
};
|
|
540
|
+
const throwError = (err) => {
|
|
541
|
+
ctx.timeline.push({ action: "throwError called", content: err });
|
|
542
|
+
throw err;
|
|
543
|
+
};
|
|
320
544
|
try {
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
545
|
+
if (target === VigorDefault)
|
|
546
|
+
throw new VigorParseError("INVALID_TARGET", {
|
|
547
|
+
method: "request",
|
|
548
|
+
data: {
|
|
549
|
+
expected: ["Response"],
|
|
550
|
+
received: target
|
|
551
|
+
},
|
|
552
|
+
context: ctx
|
|
553
|
+
});
|
|
554
|
+
ctx.timeline.push({ action: "interceptor handling: before", content: stats.interceptors.before });
|
|
555
|
+
for (const func of stats.interceptors.before) {
|
|
556
|
+
await func(ctx, { throwError });
|
|
557
|
+
}
|
|
558
|
+
if (stats.settings.raw) {
|
|
559
|
+
ctx.result = ctx.response;
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
let parsed = false;
|
|
563
|
+
for (const [i, func] of stats.strategies.funcs.length > 0
|
|
564
|
+
? stats.strategies.funcs.entries()
|
|
565
|
+
: new VigorParseStrategies().contentType()._getConfig().funcs.entries()) {
|
|
566
|
+
const cloned = (i === stats.strategies.funcs.length - 1)
|
|
567
|
+
? ctx.response
|
|
568
|
+
: ctx.response.clone();
|
|
569
|
+
try {
|
|
570
|
+
ctx.result = await func(cloned);
|
|
571
|
+
parsed = true;
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
catch { }
|
|
575
|
+
}
|
|
576
|
+
if (!parsed)
|
|
577
|
+
throw new VigorParseError("PARSER_ALL_FAILED", {
|
|
336
578
|
method: "request",
|
|
337
|
-
type: "parse_failed",
|
|
338
579
|
data: {
|
|
339
|
-
|
|
340
|
-
|
|
580
|
+
tried: stats.strategies.funcs,
|
|
581
|
+
response: ctx.response
|
|
582
|
+
},
|
|
583
|
+
context: ctx
|
|
341
584
|
});
|
|
342
|
-
return await parser();
|
|
343
585
|
}
|
|
344
|
-
|
|
345
|
-
|
|
586
|
+
const setResult = (unk) => {
|
|
587
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
588
|
+
ctx.result = unk;
|
|
589
|
+
return unk;
|
|
590
|
+
};
|
|
591
|
+
ctx.timeline.push({ action: "interceptor handling: after", content: stats.interceptors.after });
|
|
592
|
+
for (const func of stats.interceptors.after) {
|
|
593
|
+
await func(ctx, { setResult, throwError });
|
|
594
|
+
}
|
|
595
|
+
ctx.timeline.push({ action: "interceptor handling: result", content: stats.interceptors.result });
|
|
596
|
+
for (const func of stats.interceptors.result) {
|
|
597
|
+
await func(ctx, { setResult, throwError });
|
|
598
|
+
}
|
|
599
|
+
return ctx.result;
|
|
346
600
|
}
|
|
347
601
|
catch (error) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
});
|
|
602
|
+
ctx.error = error;
|
|
603
|
+
let overwritten = false;
|
|
604
|
+
const setResult = (unk) => {
|
|
605
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
606
|
+
ctx.result = unk;
|
|
607
|
+
overwritten = true;
|
|
608
|
+
return unk;
|
|
609
|
+
};
|
|
610
|
+
ctx.timeline.push({ action: "interceptor handling: onError", content: stats.interceptors.onError });
|
|
611
|
+
for (const func of stats.interceptors.onError) {
|
|
612
|
+
await func(ctx, { setResult, throwError });
|
|
613
|
+
}
|
|
614
|
+
if (overwritten)
|
|
615
|
+
return ctx.result;
|
|
616
|
+
if (stats.settings.default !== VigorDefault)
|
|
617
|
+
return stats.settings.default;
|
|
618
|
+
throw error;
|
|
357
619
|
}
|
|
358
620
|
}
|
|
359
621
|
}
|
|
360
622
|
class VigorFetchSettings extends VigorStatus {
|
|
361
|
-
constructor(config
|
|
362
|
-
|
|
363
|
-
origin: "",
|
|
364
|
-
path: [],
|
|
365
|
-
query: {},
|
|
366
|
-
unretry: [400, 401, 403, 404, 405, 413, 422],
|
|
623
|
+
constructor(config) {
|
|
624
|
+
const base = {
|
|
367
625
|
retryHeaders: ["retry-after", "ratelimit-reset", "x-ratelimit-reset", "x-retry-after", "x-amz-retry-after", "chrome-proxy-next-link"],
|
|
368
|
-
|
|
369
|
-
|
|
626
|
+
unretryStatus: [400, 401, 403, 404, 405, 413, 422],
|
|
627
|
+
default: VigorDefault
|
|
628
|
+
};
|
|
629
|
+
super(config, base, (c) => new VigorFetchSettings(c));
|
|
370
630
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
unretry(...numbers) { return this._next({ unretry: numbers.flat() }); }
|
|
375
|
-
retryHeaders(...strs) { return this._next({ retryHeaders: [...this._config.retryHeaders, ...strs.flat()] }); }
|
|
376
|
-
method(str) { return this._next({ method: str }); }
|
|
377
|
-
headers(obj) { return this._next({ headers: obj }); }
|
|
378
|
-
body(obj) { return this._next({ body: obj }); }
|
|
379
|
-
options(obj) { return this._next({ options: obj }); }
|
|
380
|
-
default(obj) { return this._next({ default: obj }); }
|
|
631
|
+
retryHeaders(...strs) { return this._next({ retryHeaders: strs.flat() }); }
|
|
632
|
+
unretryStatus(...nums) { return this._next({ unretryStatus: nums.flat() }); }
|
|
633
|
+
default(unk) { return this._next({ default: unk }); }
|
|
381
634
|
}
|
|
382
635
|
class VigorFetchInterceptors extends VigorStatus {
|
|
383
|
-
constructor(config
|
|
384
|
-
|
|
636
|
+
constructor(config) {
|
|
637
|
+
const base = {
|
|
385
638
|
before: [],
|
|
386
639
|
after: [],
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
640
|
+
result: [],
|
|
641
|
+
onError: []
|
|
642
|
+
};
|
|
643
|
+
super(config, base, (c) => new VigorFetchInterceptors(c));
|
|
390
644
|
}
|
|
391
|
-
before(...funcs) { return this._next({ before:
|
|
392
|
-
after(...funcs) { return this._next({ after:
|
|
393
|
-
|
|
394
|
-
|
|
645
|
+
before(...funcs) { return this._next({ before: funcs.flat() }); }
|
|
646
|
+
after(...funcs) { return this._next({ after: funcs.flat() }); }
|
|
647
|
+
result(...funcs) { return this._next({ result: funcs.flat() }); }
|
|
648
|
+
onError(...funcs) { return this._next({ onError: funcs.flat() }); }
|
|
395
649
|
}
|
|
396
650
|
class VigorFetch extends VigorStatus {
|
|
397
|
-
constructor(config
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
651
|
+
constructor(config) {
|
|
652
|
+
const base = {
|
|
653
|
+
origin: [],
|
|
654
|
+
path: [],
|
|
655
|
+
query: [],
|
|
656
|
+
hash: "",
|
|
657
|
+
options: {
|
|
658
|
+
headers: {},
|
|
659
|
+
body: VigorDefault
|
|
660
|
+
},
|
|
661
|
+
settings: new VigorFetchSettings()._getBase(),
|
|
662
|
+
interceptors: new VigorFetchInterceptors()._getBase(),
|
|
663
|
+
retryConfig: new VigorRetry()._getBase(),
|
|
664
|
+
parseConfig: new VigorParse()._getBase()
|
|
665
|
+
};
|
|
666
|
+
super(config, base, (c) => new VigorFetch(c));
|
|
667
|
+
}
|
|
668
|
+
_stringifyList(unkList) {
|
|
669
|
+
return unkList
|
|
670
|
+
.filter(unk => unk !== null && unk !== undefined)
|
|
671
|
+
.map(unk => {
|
|
672
|
+
if (unk instanceof Date)
|
|
673
|
+
return unk.toISOString();
|
|
674
|
+
return String(unk);
|
|
403
675
|
});
|
|
404
676
|
}
|
|
405
|
-
origin(
|
|
406
|
-
path(...strs) { return this._next({
|
|
407
|
-
query(
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
677
|
+
origin(...strs) { return this._next({ origin: this._stringifyList(strs.flat()) }); }
|
|
678
|
+
path(...strs) { return this._next({ path: this._stringifyList(strs.flat()) }); }
|
|
679
|
+
query(...strs) { return this._next({ query: strs.flat() }); }
|
|
680
|
+
hash(str) { return this._next({ hash: str }); }
|
|
681
|
+
options(obj) { return this._next({ options: obj }); }
|
|
682
|
+
headers(obj) { return this._next({ options: { headers: obj } }); }
|
|
683
|
+
body(obj) { return this._next({ options: { headers: this._config.options.headers, body: obj } }); }
|
|
684
|
+
_buildUrl(origin, path, query, hash) {
|
|
685
|
+
const originObj = new URL(origin[0]);
|
|
686
|
+
const baseStr = originObj.origin;
|
|
687
|
+
const pathObj = [originObj.pathname.replace(/^\/+|\/+$/g, '')];
|
|
688
|
+
for (const str of path) {
|
|
689
|
+
pathObj.push(str.replace(/^\/+|\/+$/g, ''));
|
|
690
|
+
}
|
|
691
|
+
const pathStr = pathObj.join('/');
|
|
692
|
+
const mainObj = new URL(pathStr, baseStr);
|
|
693
|
+
const parseVal = (val) => {
|
|
694
|
+
if (val instanceof Date)
|
|
695
|
+
return val.toISOString();
|
|
696
|
+
return String(val);
|
|
697
|
+
};
|
|
698
|
+
const queryObj = [...Array.from(originObj.searchParams.entries()), ...query.flatMap(qu => Object.entries(qu))];
|
|
699
|
+
for (const [key, val] of queryObj) {
|
|
700
|
+
if (val === undefined || val === null)
|
|
701
|
+
continue;
|
|
702
|
+
if (Array.isArray(val))
|
|
703
|
+
for (const e of val) {
|
|
704
|
+
mainObj.searchParams.append(key, parseVal(e));
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
mainObj.searchParams.append(key, parseVal(val));
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
mainObj.hash = hash ?? originObj.hash;
|
|
711
|
+
return mainObj.href;
|
|
712
|
+
}
|
|
713
|
+
_normalizeOptions(body) {
|
|
714
|
+
if (body == null)
|
|
715
|
+
return { isJson: false, headers: {}, body };
|
|
716
|
+
if (typeof body === "string")
|
|
717
|
+
return { isJson: false, headers: {
|
|
718
|
+
"Content-Type": "text/plain;charset=UTF-8"
|
|
719
|
+
}, body };
|
|
720
|
+
if (body instanceof Blob)
|
|
721
|
+
return { isJson: false, headers: {
|
|
722
|
+
...(body.type && { "Content-Type": body.type })
|
|
723
|
+
}, body };
|
|
724
|
+
if (body instanceof ArrayBuffer)
|
|
725
|
+
return { isJson: false, headers: {
|
|
726
|
+
"Content-Type": "application/octet-stream"
|
|
727
|
+
}, body };
|
|
728
|
+
if (body instanceof URLSearchParams)
|
|
729
|
+
return { isJson: false, headers: {
|
|
730
|
+
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
|
|
731
|
+
}, body };
|
|
732
|
+
if (body instanceof FormData)
|
|
733
|
+
return { isJson: false, headers: {}, body };
|
|
734
|
+
if (typeof body === "object") {
|
|
735
|
+
return { isJson: true, headers: {
|
|
736
|
+
"Content-Type": "application/json"
|
|
737
|
+
}, body: JSON.stringify(body) };
|
|
738
|
+
}
|
|
739
|
+
throw new VigorFetchError("INVALID_BODY", {
|
|
740
|
+
method: "_normalizeBody",
|
|
741
|
+
data: {
|
|
742
|
+
expected: ["string", "Blob", "ArrayBuffer", "URLSearchParams", "FormData"],
|
|
743
|
+
received: body
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
settings(func) {
|
|
748
|
+
if (typeof func === 'function') {
|
|
749
|
+
return this._next({ settings: func(new VigorFetchSettings(this._config.settings))._getConfig() });
|
|
750
|
+
}
|
|
751
|
+
return this._next({ settings: func });
|
|
414
752
|
}
|
|
415
753
|
interceptors(func) {
|
|
416
|
-
|
|
754
|
+
if (typeof func === 'function') {
|
|
755
|
+
return this._next({ interceptors: func(new VigorFetchInterceptors(this._config.interceptors))._getConfig() });
|
|
756
|
+
}
|
|
757
|
+
return this._next({ interceptors: func });
|
|
417
758
|
}
|
|
418
759
|
retryConfig(func) {
|
|
419
|
-
|
|
760
|
+
if (typeof func === 'function') {
|
|
761
|
+
return this._next({ retryConfig: func(new VigorRetry(this._config.retryConfig))._getConfig() });
|
|
762
|
+
}
|
|
763
|
+
return this._next({ retryConfig: func });
|
|
420
764
|
}
|
|
421
765
|
parseConfig(func) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
buildUrl(origin, path, query) {
|
|
425
|
-
if (!origin)
|
|
426
|
-
throw new VigorFetchError("INVALID_URL", {
|
|
427
|
-
method: "buildUrl",
|
|
428
|
-
data: {
|
|
429
|
-
received: origin
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
const url = new URL(origin);
|
|
433
|
-
const segments = [
|
|
434
|
-
url.pathname,
|
|
435
|
-
...path
|
|
436
|
-
]
|
|
437
|
-
.flat()
|
|
438
|
-
.filter(Boolean)
|
|
439
|
-
.flatMap(p => p.split('/'))
|
|
440
|
-
.filter(Boolean);
|
|
441
|
-
url.pathname = '/' + segments.join('/');
|
|
442
|
-
const params = new URLSearchParams(url.search);
|
|
443
|
-
for (const [k, v] of Object.entries(query ?? {})) {
|
|
444
|
-
if (v == null)
|
|
445
|
-
continue;
|
|
446
|
-
if (Array.isArray(v)) {
|
|
447
|
-
v.forEach(i => params.append(k, String(i)));
|
|
448
|
-
}
|
|
449
|
-
else {
|
|
450
|
-
params.set(k, String(v));
|
|
451
|
-
}
|
|
766
|
+
if (typeof func === 'function') {
|
|
767
|
+
return this._next({ parseConfig: func(new VigorParse(this._config.parseConfig))._getConfig() });
|
|
452
768
|
}
|
|
453
|
-
|
|
454
|
-
return url.toString();
|
|
769
|
+
return this._next({ parseConfig: func });
|
|
455
770
|
}
|
|
456
|
-
async request() {
|
|
457
|
-
const
|
|
771
|
+
async request(config, timeline = []) {
|
|
772
|
+
const stats = this._mergeConfig(this._config, config);
|
|
458
773
|
let ctx = {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
onError: [...config.retryConfig.interceptors.onError],
|
|
466
|
-
onRetry: [...config.retryConfig.interceptors.onRetry],
|
|
467
|
-
retryIf: [...config.retryConfig.interceptors.retryIf],
|
|
468
|
-
}
|
|
774
|
+
href: "",
|
|
775
|
+
result: VigorDefault,
|
|
776
|
+
response: VigorDefault,
|
|
777
|
+
options: {
|
|
778
|
+
headers: VigorDefault,
|
|
779
|
+
body: VigorDefault
|
|
469
780
|
},
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
result: [...config.interceptors.result]
|
|
478
|
-
},
|
|
479
|
-
runtime: {
|
|
480
|
-
result: EMPTY
|
|
481
|
-
}
|
|
781
|
+
error: VigorDefault,
|
|
782
|
+
timeline: timeline,
|
|
783
|
+
stats,
|
|
784
|
+
};
|
|
785
|
+
const throwError = (err) => {
|
|
786
|
+
ctx.timeline.push({ action: "throwError called", content: err });
|
|
787
|
+
throw err;
|
|
482
788
|
};
|
|
483
|
-
const throwError = (error) => { throw error; };
|
|
484
789
|
try {
|
|
485
|
-
|
|
486
|
-
|
|
790
|
+
try {
|
|
791
|
+
new URL(stats.origin[0]);
|
|
792
|
+
}
|
|
793
|
+
catch {
|
|
487
794
|
throw new VigorFetchError("INVALID_PROTOCOL", {
|
|
488
795
|
method: "request",
|
|
489
796
|
data: {
|
|
490
|
-
expected: ["
|
|
491
|
-
received:
|
|
797
|
+
expected: ["valid URL protocol"],
|
|
798
|
+
received: stats.origin
|
|
492
799
|
}
|
|
493
800
|
});
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
...ctx.setting.options,
|
|
501
|
-
signal: null
|
|
801
|
+
}
|
|
802
|
+
ctx.href = this._buildUrl(stats.origin, stats.path, stats.query, stats.hash);
|
|
803
|
+
const { headers, body, ...others } = stats.options;
|
|
804
|
+
ctx.options = {
|
|
805
|
+
...others,
|
|
806
|
+
headers: {}
|
|
502
807
|
};
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
808
|
+
const hasBody = body !== VigorDefault &&
|
|
809
|
+
body !== undefined;
|
|
810
|
+
if (hasBody) {
|
|
811
|
+
const normalized = this._normalizeOptions(body);
|
|
812
|
+
if (normalized.body !== undefined) {
|
|
813
|
+
ctx.options.body = normalized.body;
|
|
814
|
+
}
|
|
815
|
+
Object.assign(ctx.options.headers, normalized.headers);
|
|
816
|
+
}
|
|
817
|
+
Object.assign(ctx.options.headers, headers);
|
|
818
|
+
ctx.timeline.push({ action: "options set", content: ctx.options });
|
|
819
|
+
const setOptions = (unk) => {
|
|
820
|
+
ctx.timeline.push({ action: "setOptions called", content: unk });
|
|
821
|
+
return ctx.options = unk;
|
|
822
|
+
};
|
|
823
|
+
const setHeaders = (unk) => {
|
|
824
|
+
ctx.timeline.push({ action: "setHeaders called", content: unk });
|
|
825
|
+
return ctx.options.headers = unk;
|
|
826
|
+
};
|
|
827
|
+
const setBody = (unk) => {
|
|
828
|
+
ctx.timeline.push({ action: "setBody called", content: unk });
|
|
829
|
+
return ctx.options.body = unk;
|
|
510
830
|
};
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
831
|
+
const fetchTask = async (ctx2, { abort, signal }) => {
|
|
832
|
+
ctx.options.signal = signal;
|
|
833
|
+
const result = await fetch(ctx.href, ctx.options);
|
|
834
|
+
return result;
|
|
835
|
+
};
|
|
836
|
+
const throwStatus = async (ctx2, api) => {
|
|
837
|
+
const response = ctx2.result;
|
|
838
|
+
if (!response.ok) {
|
|
839
|
+
api.throwError(new VigorFetchError("FETCH_FAILED", {
|
|
515
840
|
method: "request",
|
|
516
|
-
type: "fetch_error",
|
|
517
841
|
data: {
|
|
518
|
-
status:
|
|
519
|
-
|
|
520
|
-
url:
|
|
842
|
+
status: response.status,
|
|
843
|
+
response: response,
|
|
844
|
+
url: response.url,
|
|
845
|
+
headers: response.headers,
|
|
846
|
+
body: response.body,
|
|
847
|
+
statusText: response.statusText
|
|
521
848
|
}
|
|
522
849
|
}));
|
|
850
|
+
}
|
|
523
851
|
};
|
|
524
|
-
const handleBlacklist = (ctx2,
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
852
|
+
const handleBlacklist = async (ctx2, api) => {
|
|
853
|
+
const response = ctx2.result;
|
|
854
|
+
ctx.error = ctx2.error;
|
|
855
|
+
if (response instanceof Response) {
|
|
856
|
+
if (stats.settings.unretryStatus.includes(response.status))
|
|
857
|
+
api.cancelRetry();
|
|
858
|
+
else
|
|
859
|
+
api.proceedRetry();
|
|
860
|
+
}
|
|
528
861
|
};
|
|
529
|
-
const
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
862
|
+
const handleRatelimit = async (ctx2, api) => {
|
|
863
|
+
const response = ctx2.result;
|
|
864
|
+
ctx.error = ctx2.error;
|
|
865
|
+
if (response instanceof Response) {
|
|
866
|
+
if (response.status === 429) {
|
|
867
|
+
let retryHeader = null;
|
|
868
|
+
for (const header of stats.settings.retryHeaders) {
|
|
869
|
+
retryHeader = response.headers.get(header);
|
|
870
|
+
if (retryHeader)
|
|
871
|
+
break;
|
|
872
|
+
}
|
|
873
|
+
if (retryHeader) {
|
|
874
|
+
const toNumber = Number(retryHeader);
|
|
875
|
+
const delay = !isNaN(toNumber)
|
|
876
|
+
? toNumber * 1000
|
|
877
|
+
: (() => {
|
|
878
|
+
const toDate = new Date(retryHeader).getTime();
|
|
879
|
+
return !isNaN(toDate)
|
|
880
|
+
? toDate - Date.now()
|
|
881
|
+
: null;
|
|
882
|
+
})();
|
|
883
|
+
if (delay !== null && delay > 0)
|
|
884
|
+
api.setDelay(delay + Math.random() * ctx2.stats.settings.jitter);
|
|
885
|
+
}
|
|
536
886
|
}
|
|
537
887
|
}
|
|
538
888
|
};
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
for (const func of
|
|
547
|
-
await func(ctx, { setOptions,
|
|
889
|
+
stats.retryConfig.interceptors.after.unshift(throwStatus);
|
|
890
|
+
stats.retryConfig.interceptors.retryIf.unshift(handleBlacklist);
|
|
891
|
+
stats.retryConfig.interceptors.onRetry.unshift(handleRatelimit);
|
|
892
|
+
const retryEngine = new VigorRetry(stats.retryConfig)
|
|
893
|
+
.target(fetchTask);
|
|
894
|
+
const parseEngine = new VigorParse(stats.parseConfig);
|
|
895
|
+
ctx.timeline.push({ action: "interceptor handling: before", content: stats.interceptors.before });
|
|
896
|
+
for (const func of stats.interceptors.before) {
|
|
897
|
+
await func(ctx, { throwError, setOptions, setHeaders, setBody });
|
|
548
898
|
}
|
|
549
|
-
ctx.
|
|
550
|
-
|
|
551
|
-
|
|
899
|
+
ctx.response = await retryEngine.request(undefined, timeline);
|
|
900
|
+
ctx.result = await parseEngine.target(ctx.response).request(undefined, timeline);
|
|
901
|
+
const setResult = (unk) => {
|
|
902
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
903
|
+
ctx.result = unk;
|
|
904
|
+
return unk;
|
|
905
|
+
};
|
|
906
|
+
ctx.timeline.push({ action: "interceptor handling: after", content: stats.interceptors.after });
|
|
907
|
+
for (const func of stats.interceptors.after) {
|
|
908
|
+
await func(ctx, { setResult, throwError });
|
|
552
909
|
}
|
|
553
|
-
ctx.
|
|
554
|
-
const
|
|
555
|
-
for (const func of ctx.interceptors.result) {
|
|
910
|
+
ctx.timeline.push({ action: "interceptor handling: result", content: stats.interceptors.result });
|
|
911
|
+
for (const func of stats.interceptors.result) {
|
|
556
912
|
await func(ctx, { setResult, throwError });
|
|
557
913
|
}
|
|
558
|
-
return ctx.
|
|
914
|
+
return ctx.result;
|
|
559
915
|
}
|
|
560
916
|
catch (error) {
|
|
561
|
-
ctx.
|
|
562
|
-
let
|
|
563
|
-
const setResult = (
|
|
564
|
-
|
|
565
|
-
|
|
917
|
+
ctx.error = error;
|
|
918
|
+
let overwritten = false;
|
|
919
|
+
const setResult = (unk) => {
|
|
920
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
921
|
+
ctx.result = unk;
|
|
922
|
+
overwritten = true;
|
|
923
|
+
return unk;
|
|
924
|
+
};
|
|
925
|
+
let restarted = false;
|
|
926
|
+
const restart = () => {
|
|
927
|
+
ctx.timeline.push({ action: "restart called" });
|
|
928
|
+
restarted = true;
|
|
929
|
+
};
|
|
930
|
+
ctx.timeline.push({ action: "interceptor handling: onError", content: stats.interceptors.onError });
|
|
931
|
+
for (const func of stats.interceptors.onError) {
|
|
932
|
+
await func(ctx, { setResult, throwError, restart });
|
|
566
933
|
}
|
|
567
|
-
if (
|
|
568
|
-
return
|
|
569
|
-
|
|
570
|
-
|
|
934
|
+
if (restarted) {
|
|
935
|
+
return await this.request(stats, timeline);
|
|
936
|
+
}
|
|
937
|
+
if (overwritten)
|
|
938
|
+
return ctx.result;
|
|
939
|
+
if (stats.settings.default !== VigorDefault)
|
|
940
|
+
return stats.settings.default;
|
|
571
941
|
throw error;
|
|
572
942
|
}
|
|
573
943
|
}
|
|
574
944
|
}
|
|
575
945
|
class VigorAllSettings extends VigorStatus {
|
|
576
|
-
constructor(config
|
|
577
|
-
|
|
946
|
+
constructor(config) {
|
|
947
|
+
const base = {
|
|
578
948
|
concurrency: 5,
|
|
579
|
-
jitter: 1000,
|
|
580
949
|
onlySuccess: false
|
|
581
|
-
}
|
|
950
|
+
};
|
|
951
|
+
super(config, base, (c) => new VigorAllSettings(c));
|
|
582
952
|
}
|
|
583
953
|
concurrency(num) { return this._next({ concurrency: num }); }
|
|
584
|
-
|
|
585
|
-
onlySuccess(bool) { return this._next({ onlySuccess: bool }); }
|
|
954
|
+
onlySuccess(num) { return this._next({ onlySuccess: num }); }
|
|
586
955
|
}
|
|
587
956
|
class VigorAllInterceptors extends VigorStatus {
|
|
588
|
-
constructor(config
|
|
589
|
-
|
|
957
|
+
constructor(config) {
|
|
958
|
+
const base = {
|
|
590
959
|
before: [],
|
|
591
960
|
after: [],
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
961
|
+
result: [],
|
|
962
|
+
onError: []
|
|
963
|
+
};
|
|
964
|
+
super(config, base, (c) => new VigorAllInterceptors(c));
|
|
595
965
|
}
|
|
596
|
-
before(...funcs) { return this._next({ before:
|
|
597
|
-
after(...funcs) { return this._next({ after:
|
|
598
|
-
|
|
599
|
-
|
|
966
|
+
before(...funcs) { return this._next({ before: funcs.flat() }); }
|
|
967
|
+
after(...funcs) { return this._next({ after: funcs.flat() }); }
|
|
968
|
+
result(...funcs) { return this._next({ result: funcs.flat() }); }
|
|
969
|
+
onError(...funcs) { return this._next({ onError: funcs.flat() }); }
|
|
600
970
|
}
|
|
601
971
|
class VigorAll extends VigorStatus {
|
|
602
|
-
constructor(config
|
|
603
|
-
|
|
972
|
+
constructor(config) {
|
|
973
|
+
const base = {
|
|
604
974
|
target: [],
|
|
605
|
-
|
|
606
|
-
interceptors: new VigorAllInterceptors().
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
_transfer(config) {
|
|
610
|
-
return new VigorAll({
|
|
611
|
-
...this._config,
|
|
612
|
-
...config
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
target(...args) {
|
|
616
|
-
const flat = args.flat();
|
|
617
|
-
return this._transfer({ target: flat });
|
|
975
|
+
settings: new VigorAllSettings()._getBase(),
|
|
976
|
+
interceptors: new VigorAllInterceptors()._getBase()
|
|
977
|
+
};
|
|
978
|
+
super(config, base, (c) => new VigorAll(c));
|
|
618
979
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
980
|
+
target(...funcs) { return this._next({ target: funcs.flat() }); }
|
|
981
|
+
settings(func) {
|
|
982
|
+
if (typeof func === 'function') {
|
|
983
|
+
return this._next({ settings: func(new VigorAllSettings(this._config.settings))._getConfig() });
|
|
984
|
+
}
|
|
985
|
+
return this._next({ settings: func });
|
|
623
986
|
}
|
|
624
987
|
interceptors(func) {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
}
|
|
988
|
+
if (typeof func === 'function') {
|
|
989
|
+
return this._next({ interceptors: func(new VigorAllInterceptors(this._config.interceptors))._getConfig() });
|
|
990
|
+
}
|
|
991
|
+
return this._next({ interceptors: func });
|
|
628
992
|
}
|
|
629
|
-
async
|
|
630
|
-
const config = this._config;
|
|
993
|
+
async runTask(task, { stats, root }, semaphore) {
|
|
631
994
|
let ctx = {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
},
|
|
640
|
-
runtime: {
|
|
641
|
-
tasks: [],
|
|
642
|
-
result: [],
|
|
643
|
-
}
|
|
995
|
+
result: VigorDefault,
|
|
996
|
+
error: VigorDefault,
|
|
997
|
+
timeline: [],
|
|
998
|
+
stats,
|
|
999
|
+
root,
|
|
1000
|
+
target: task,
|
|
1001
|
+
semaphore
|
|
644
1002
|
};
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
let active = 0;
|
|
651
|
-
const queue = [];
|
|
652
|
-
const runTask = async (task) => {
|
|
653
|
-
await new Promise(resolve => {
|
|
654
|
-
if (active < config.setting.concurrency) {
|
|
655
|
-
active++;
|
|
656
|
-
resolve();
|
|
657
|
-
}
|
|
658
|
-
else {
|
|
659
|
-
queue.push(() => {
|
|
660
|
-
active++;
|
|
661
|
-
resolve();
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
});
|
|
665
|
-
const throwError = (error) => { throw error; };
|
|
666
|
-
let ctxTask = {
|
|
667
|
-
target: task,
|
|
668
|
-
runtime: {
|
|
669
|
-
result: EMPTY,
|
|
670
|
-
error: null,
|
|
671
|
-
jitter: VigorRetryBackoff.randomJitter(config.setting.jitter)
|
|
672
|
-
}
|
|
673
|
-
};
|
|
1003
|
+
const throwError = (err) => {
|
|
1004
|
+
ctx.timeline.push({ action: "throwError called", content: err });
|
|
1005
|
+
throw err;
|
|
1006
|
+
};
|
|
1007
|
+
try {
|
|
674
1008
|
try {
|
|
675
|
-
await
|
|
676
|
-
|
|
677
|
-
|
|
1009
|
+
await semaphore.acquire();
|
|
1010
|
+
ctx.timeline.push({ action: "task acquired", content: ctx.target });
|
|
1011
|
+
ctx.timeline.push({ action: "interceptor handling: before", content: stats.interceptors.before });
|
|
1012
|
+
for (const func of stats.interceptors.before) {
|
|
1013
|
+
await func(ctx, { throwError });
|
|
678
1014
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
for (const func of config.interceptors.after) {
|
|
682
|
-
await func(ctxTask, { setResult, throwError });
|
|
683
|
-
}
|
|
684
|
-
if (ctxTask.runtime.result === EMPTY) {
|
|
685
|
-
throw new VigorAllError("RESULT_NOT_SET", {
|
|
686
|
-
method: "request",
|
|
687
|
-
data: undefined
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
return ctxTask.runtime.result;
|
|
1015
|
+
ctx.timeline.push({ action: "task started", content: ctx.target });
|
|
1016
|
+
ctx.result = await task(ctx);
|
|
691
1017
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
return
|
|
1018
|
+
finally {
|
|
1019
|
+
ctx.timeline.push({ action: "task ended", content: ctx.target });
|
|
1020
|
+
const setResult = (unk) => {
|
|
1021
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
1022
|
+
ctx.result = unk;
|
|
1023
|
+
return unk;
|
|
698
1024
|
};
|
|
699
|
-
|
|
700
|
-
|
|
1025
|
+
ctx.timeline.push({ action: "interceptor handling: after", content: stats.interceptors.after });
|
|
1026
|
+
for (const func of stats.interceptors.after) {
|
|
1027
|
+
await func(ctx, { setResult, throwError });
|
|
701
1028
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
1029
|
+
semaphore.release();
|
|
1030
|
+
ctx.timeline.push({ action: "task released", content: ctx.target });
|
|
1031
|
+
return ctx.result;
|
|
705
1032
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
1033
|
+
}
|
|
1034
|
+
catch (error) {
|
|
1035
|
+
ctx.error = error;
|
|
1036
|
+
let overwritten = false;
|
|
1037
|
+
const setResult = (unk) => {
|
|
1038
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
1039
|
+
ctx.result = unk;
|
|
1040
|
+
overwritten = true;
|
|
1041
|
+
return unk;
|
|
1042
|
+
};
|
|
1043
|
+
ctx.timeline.push({ action: "interceptor handling: onError", content: stats.interceptors.onError });
|
|
1044
|
+
for (const func of stats.interceptors.onError) {
|
|
1045
|
+
await func(ctx, { setResult, throwError });
|
|
711
1046
|
}
|
|
1047
|
+
if (overwritten)
|
|
1048
|
+
return ctx.result;
|
|
1049
|
+
throw error;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
async request(config, timeline = []) {
|
|
1053
|
+
const stats = this._mergeConfig(this._config, config);
|
|
1054
|
+
let ctx = {
|
|
1055
|
+
result: VigorDefault,
|
|
1056
|
+
timeline,
|
|
1057
|
+
stats,
|
|
1058
|
+
queue: new Set()
|
|
712
1059
|
};
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
const isFailed = Symbol("FAILED");
|
|
716
|
-
ctx.runtime.result = settled.map((res, idx) => {
|
|
717
|
-
if (res.status === "fulfilled")
|
|
718
|
-
return res.value;
|
|
719
|
-
if (ctx.setting.onlySuccess)
|
|
720
|
-
return isFailed;
|
|
721
|
-
return new VigorAllError("REQUEST_FAILED", {
|
|
1060
|
+
if (stats.target.length === 0)
|
|
1061
|
+
throw new VigorAllError("EMPTY_TARGET", {
|
|
722
1062
|
method: "request",
|
|
723
|
-
data: {
|
|
724
|
-
index: idx,
|
|
725
|
-
error: normalizeError(res?.reason || "unknown")
|
|
726
|
-
}
|
|
1063
|
+
data: {}
|
|
727
1064
|
});
|
|
728
|
-
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
1065
|
+
const waitQueue = [];
|
|
1066
|
+
for (const task of stats.target) {
|
|
1067
|
+
const acquire = () => {
|
|
1068
|
+
if (ctx.queue.size < stats.settings.concurrency) {
|
|
1069
|
+
return Promise.resolve();
|
|
1070
|
+
}
|
|
1071
|
+
return new Promise((res) => waitQueue.push(res));
|
|
1072
|
+
};
|
|
1073
|
+
const release = () => {
|
|
1074
|
+
if (waitQueue.length > 0) {
|
|
1075
|
+
const next = waitQueue.shift();
|
|
1076
|
+
if (next)
|
|
1077
|
+
next();
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
acquire();
|
|
1081
|
+
let promise;
|
|
1082
|
+
promise = this.runTask(task, { stats, root: ctx }, { acquire, release }).then(res => {
|
|
1083
|
+
ctx.queue.delete(promise);
|
|
1084
|
+
return { success: true, value: res };
|
|
1085
|
+
}).catch(err => ({ success: false, value: err }));
|
|
1086
|
+
ctx.queue.add(promise);
|
|
1087
|
+
}
|
|
1088
|
+
const raw = await Promise.all(ctx.queue);
|
|
1089
|
+
ctx.result = stats.settings.onlySuccess
|
|
1090
|
+
? raw.filter(r => r.success).map(r => r.value)
|
|
1091
|
+
: raw.map(r => r.value);
|
|
1092
|
+
const setResult = (unk) => {
|
|
1093
|
+
ctx.timeline.push({ action: "setResult called", content: unk });
|
|
1094
|
+
ctx.result = unk;
|
|
1095
|
+
return unk;
|
|
1096
|
+
};
|
|
1097
|
+
const throwError = (err) => {
|
|
1098
|
+
ctx.timeline.push({ action: "throwError called", content: err });
|
|
1099
|
+
throw err;
|
|
1100
|
+
};
|
|
1101
|
+
ctx.timeline.push({ action: "interceptor handling: result", content: stats.interceptors.result });
|
|
1102
|
+
for (const func of stats.interceptors.result) {
|
|
732
1103
|
await func(ctx, { setResult, throwError });
|
|
733
1104
|
}
|
|
734
|
-
return ctx.
|
|
1105
|
+
return ctx.result;
|
|
735
1106
|
}
|
|
736
1107
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
return this.registry.VigorFetch.main().origin(origin);
|
|
769
|
-
}
|
|
770
|
-
all(...tasks) {
|
|
771
|
-
return this.registry.VigorAll
|
|
772
|
-
.main()
|
|
773
|
-
.target(...tasks);
|
|
774
|
-
}
|
|
775
|
-
parse(response) {
|
|
776
|
-
return this.registry.VigorParse.main().target(response);
|
|
777
|
-
}
|
|
778
|
-
retry(fn) {
|
|
779
|
-
return this.registry.VigorRetry.main().target(fn);
|
|
1108
|
+
const VigorEntry = {
|
|
1109
|
+
retry: {
|
|
1110
|
+
main: VigorRetry,
|
|
1111
|
+
settings: VigorRetrySettings,
|
|
1112
|
+
interceptors: VigorRetryInterceptors,
|
|
1113
|
+
error: VigorRetryError,
|
|
1114
|
+
algorithms: {
|
|
1115
|
+
constant: VigorRetryAlgorithmsConstant,
|
|
1116
|
+
linear: VigorRetryAlgorithmsLinear,
|
|
1117
|
+
backoff: VigorRetryAlgorithmsBackoff,
|
|
1118
|
+
custom: VigorRetryAlgorithmsCustom
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
parse: {
|
|
1122
|
+
main: VigorParse,
|
|
1123
|
+
settings: VigorParseSettings,
|
|
1124
|
+
interceptors: VigorParseInterceptors,
|
|
1125
|
+
error: VigorParseError,
|
|
1126
|
+
strategies: VigorParseStrategies
|
|
1127
|
+
},
|
|
1128
|
+
fetch: {
|
|
1129
|
+
main: VigorFetch,
|
|
1130
|
+
settings: VigorFetchSettings,
|
|
1131
|
+
interceptors: VigorFetchInterceptors,
|
|
1132
|
+
error: VigorFetchError,
|
|
1133
|
+
},
|
|
1134
|
+
all: {
|
|
1135
|
+
main: VigorAll,
|
|
1136
|
+
settings: VigorAllSettings,
|
|
1137
|
+
interceptors: VigorAllInterceptors,
|
|
1138
|
+
error: VigorAllError
|
|
780
1139
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
1140
|
+
};
|
|
1141
|
+
const vigor = {
|
|
1142
|
+
use: async (func, config) => {
|
|
1143
|
+
return await func(VigorEntry, config);
|
|
1144
|
+
},
|
|
1145
|
+
fetch: (...strs) => {
|
|
1146
|
+
return new VigorFetch().origin(...strs);
|
|
1147
|
+
},
|
|
1148
|
+
retry: (target) => {
|
|
1149
|
+
return new VigorRetry().target(target);
|
|
1150
|
+
},
|
|
1151
|
+
parse: (response) => {
|
|
1152
|
+
return new VigorParse().target(response);
|
|
1153
|
+
},
|
|
1154
|
+
all: (...funcs) => {
|
|
1155
|
+
return new VigorAll().target(...funcs);
|
|
1156
|
+
},
|
|
1157
|
+
builder: {
|
|
1158
|
+
fetch: {
|
|
1159
|
+
settings: (c) => new VigorFetchSettings(c),
|
|
1160
|
+
interceptors: (c) => new VigorFetchInterceptors(c),
|
|
1161
|
+
},
|
|
1162
|
+
retry: {
|
|
1163
|
+
settings: (c) => new VigorRetrySettings(c),
|
|
1164
|
+
interceptors: (c) => new VigorRetryInterceptors(c),
|
|
1165
|
+
},
|
|
1166
|
+
parse: {
|
|
1167
|
+
settings: (c) => new VigorParseSettings(c),
|
|
1168
|
+
interceptors: (c) => new VigorParseInterceptors(c),
|
|
1169
|
+
},
|
|
1170
|
+
all: {
|
|
1171
|
+
settings: (c) => new VigorAllSettings(c),
|
|
1172
|
+
interceptors: (c) => new VigorAllInterceptors(c),
|
|
1173
|
+
}
|
|
801
1174
|
}
|
|
802
|
-
}
|
|
803
|
-
const vigor = new Vigor();
|
|
1175
|
+
};
|
|
804
1176
|
|
|
805
|
-
exports.
|
|
806
|
-
exports.VigorAll = VigorAll;
|
|
807
|
-
exports.VigorAllError = VigorAllError;
|
|
808
|
-
exports.VigorAllInterceptors = VigorAllInterceptors;
|
|
809
|
-
exports.VigorAllSettings = VigorAllSettings;
|
|
810
|
-
exports.VigorFetch = VigorFetch;
|
|
811
|
-
exports.VigorFetchError = VigorFetchError;
|
|
812
|
-
exports.VigorFetchInterceptors = VigorFetchInterceptors;
|
|
813
|
-
exports.VigorFetchSettings = VigorFetchSettings;
|
|
814
|
-
exports.VigorParse = VigorParse;
|
|
815
|
-
exports.VigorParseError = VigorParseError;
|
|
816
|
-
exports.VigorRetry = VigorRetry;
|
|
817
|
-
exports.VigorRetryBackoff = VigorRetryBackoff;
|
|
818
|
-
exports.VigorRetryError = VigorRetryError;
|
|
819
|
-
exports.VigorRetryInterceptors = VigorRetryInterceptors;
|
|
820
|
-
exports.VigorRetrySettings = VigorRetrySettings;
|
|
1177
|
+
exports.VigorEntry = VigorEntry;
|
|
821
1178
|
exports.default = vigor;
|
|
822
1179
|
exports.vigor = vigor;
|