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