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.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,743 +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
|
-
interceptors: new VigorRetryInterceptors().getBase(),
|
|
129
|
-
controller: config.controller || new AbortController()
|
|
130
|
-
});
|
|
121
|
+
class VigorRetryAlgorithmsConstant extends VigorStatus {
|
|
122
|
+
constructor(config) {
|
|
123
|
+
const base = {
|
|
124
|
+
interval: 2000
|
|
125
|
+
};
|
|
126
|
+
super(config, base, (c) => new VigorRetryAlgorithmsConstant(c));
|
|
131
127
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
interval(num) { return this._next({ interval: num }); }
|
|
129
|
+
/** @internal */
|
|
130
|
+
_calculateDelay(attempt) {
|
|
131
|
+
return this._config.interval;
|
|
135
132
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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));
|
|
140
152
|
}
|
|
141
|
-
|
|
142
|
-
|
|
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));
|
|
164
|
+
}
|
|
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)));
|
|
174
|
+
}
|
|
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));
|
|
184
|
+
}
|
|
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) });
|
|
146
225
|
}
|
|
147
|
-
|
|
148
|
-
|
|
226
|
+
abortSignals(...abortSignals) {
|
|
227
|
+
return this._next({ abortSignals: abortSignals.flat() });
|
|
228
|
+
}
|
|
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() });
|
|
517
|
+
}
|
|
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() });
|
|
313
523
|
}
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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", {
|
|
332
574
|
method: "request",
|
|
333
|
-
type: "parse_failed",
|
|
334
575
|
data: {
|
|
335
|
-
|
|
336
|
-
|
|
576
|
+
tried: stats.strategies.funcs,
|
|
577
|
+
response: ctx.response
|
|
578
|
+
},
|
|
579
|
+
context: ctx
|
|
337
580
|
});
|
|
338
|
-
return await parser();
|
|
339
581
|
}
|
|
340
|
-
|
|
341
|
-
|
|
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;
|
|
342
596
|
}
|
|
343
597
|
catch (error) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
});
|
|
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;
|
|
353
615
|
}
|
|
354
616
|
}
|
|
355
617
|
}
|
|
356
618
|
class VigorFetchSettings extends VigorStatus {
|
|
357
|
-
constructor(config
|
|
358
|
-
|
|
359
|
-
origin: "",
|
|
360
|
-
path: [],
|
|
361
|
-
query: {},
|
|
362
|
-
unretry: [400, 401, 403, 404, 405, 413, 422],
|
|
619
|
+
constructor(config) {
|
|
620
|
+
const base = {
|
|
363
621
|
retryHeaders: ["retry-after", "ratelimit-reset", "x-ratelimit-reset", "x-retry-after", "x-amz-retry-after", "chrome-proxy-next-link"],
|
|
364
|
-
|
|
365
|
-
|
|
622
|
+
unretryStatus: [400, 401, 403, 404, 405, 413, 422],
|
|
623
|
+
default: VigorDefault
|
|
624
|
+
};
|
|
625
|
+
super(config, base, (c) => new VigorFetchSettings(c));
|
|
366
626
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
unretry(...numbers) { return this._next({ unretry: numbers.flat() }); }
|
|
371
|
-
retryHeaders(...strs) { return this._next({ retryHeaders: [...this._config.retryHeaders, ...strs.flat()] }); }
|
|
372
|
-
method(str) { return this._next({ method: str }); }
|
|
373
|
-
headers(obj) { return this._next({ headers: obj }); }
|
|
374
|
-
body(obj) { return this._next({ body: obj }); }
|
|
375
|
-
options(obj) { return this._next({ options: obj }); }
|
|
376
|
-
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 }); }
|
|
377
630
|
}
|
|
378
631
|
class VigorFetchInterceptors extends VigorStatus {
|
|
379
|
-
constructor(config
|
|
380
|
-
|
|
632
|
+
constructor(config) {
|
|
633
|
+
const base = {
|
|
381
634
|
before: [],
|
|
382
635
|
after: [],
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
636
|
+
result: [],
|
|
637
|
+
onError: []
|
|
638
|
+
};
|
|
639
|
+
super(config, base, (c) => new VigorFetchInterceptors(c));
|
|
386
640
|
}
|
|
387
|
-
before(...funcs) { return this._next({ before:
|
|
388
|
-
after(...funcs) { return this._next({ after:
|
|
389
|
-
|
|
390
|
-
|
|
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() }); }
|
|
391
645
|
}
|
|
392
646
|
class VigorFetch extends VigorStatus {
|
|
393
|
-
constructor(config
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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);
|
|
399
671
|
});
|
|
400
672
|
}
|
|
401
|
-
origin(
|
|
402
|
-
path(...strs) { return this._next({
|
|
403
|
-
query(
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
+
}
|
|
741
|
+
});
|
|
742
|
+
}
|
|
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 });
|
|
410
748
|
}
|
|
411
749
|
interceptors(func) {
|
|
412
|
-
|
|
750
|
+
if (typeof func === 'function') {
|
|
751
|
+
return this._next({ interceptors: func(new VigorFetchInterceptors(this._config.interceptors))._getConfig() });
|
|
752
|
+
}
|
|
753
|
+
return this._next({ interceptors: func });
|
|
413
754
|
}
|
|
414
755
|
retryConfig(func) {
|
|
415
|
-
|
|
756
|
+
if (typeof func === 'function') {
|
|
757
|
+
return this._next({ retryConfig: func(new VigorRetry(this._config.retryConfig))._getConfig() });
|
|
758
|
+
}
|
|
759
|
+
return this._next({ retryConfig: func });
|
|
416
760
|
}
|
|
417
761
|
parseConfig(func) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
buildUrl(origin, path, query) {
|
|
421
|
-
if (!origin)
|
|
422
|
-
throw new VigorFetchError("INVALID_URL", {
|
|
423
|
-
method: "buildUrl",
|
|
424
|
-
data: {
|
|
425
|
-
received: origin
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
const url = new URL(origin);
|
|
429
|
-
const segments = [
|
|
430
|
-
url.pathname,
|
|
431
|
-
...path
|
|
432
|
-
]
|
|
433
|
-
.flat()
|
|
434
|
-
.filter(Boolean)
|
|
435
|
-
.flatMap(p => p.split('/'))
|
|
436
|
-
.filter(Boolean);
|
|
437
|
-
url.pathname = '/' + segments.join('/');
|
|
438
|
-
const params = new URLSearchParams(url.search);
|
|
439
|
-
for (const [k, v] of Object.entries(query ?? {})) {
|
|
440
|
-
if (v == null)
|
|
441
|
-
continue;
|
|
442
|
-
if (Array.isArray(v)) {
|
|
443
|
-
v.forEach(i => params.append(k, String(i)));
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
params.set(k, String(v));
|
|
447
|
-
}
|
|
762
|
+
if (typeof func === 'function') {
|
|
763
|
+
return this._next({ parseConfig: func(new VigorParse(this._config.parseConfig))._getConfig() });
|
|
448
764
|
}
|
|
449
|
-
|
|
450
|
-
return url.toString();
|
|
765
|
+
return this._next({ parseConfig: func });
|
|
451
766
|
}
|
|
452
|
-
async request() {
|
|
453
|
-
const
|
|
767
|
+
async request(config, timeline = []) {
|
|
768
|
+
const stats = this._mergeConfig(this._config, config);
|
|
454
769
|
let ctx = {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
onError: [...config.retryConfig.interceptors.onError],
|
|
462
|
-
onRetry: [...config.retryConfig.interceptors.onRetry],
|
|
463
|
-
retryIf: [...config.retryConfig.interceptors.retryIf],
|
|
464
|
-
}
|
|
770
|
+
href: "",
|
|
771
|
+
result: VigorDefault,
|
|
772
|
+
response: VigorDefault,
|
|
773
|
+
options: {
|
|
774
|
+
headers: VigorDefault,
|
|
775
|
+
body: VigorDefault
|
|
465
776
|
},
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
result: [...config.interceptors.result]
|
|
474
|
-
},
|
|
475
|
-
runtime: {
|
|
476
|
-
result: EMPTY
|
|
477
|
-
}
|
|
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;
|
|
478
784
|
};
|
|
479
|
-
const throwError = (error) => { throw error; };
|
|
480
785
|
try {
|
|
481
|
-
|
|
482
|
-
|
|
786
|
+
try {
|
|
787
|
+
new URL(stats.origin[0]);
|
|
788
|
+
}
|
|
789
|
+
catch {
|
|
483
790
|
throw new VigorFetchError("INVALID_PROTOCOL", {
|
|
484
791
|
method: "request",
|
|
485
792
|
data: {
|
|
486
|
-
expected: ["
|
|
487
|
-
received:
|
|
793
|
+
expected: ["valid URL protocol"],
|
|
794
|
+
received: stats.origin
|
|
488
795
|
}
|
|
489
796
|
});
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
...ctx.setting.options,
|
|
497
|
-
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: {}
|
|
498
803
|
};
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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;
|
|
506
826
|
};
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
827
|
+
const fetchTask = async (ctx2, { abort, signal }) => {
|
|
828
|
+
ctx.options.signal = signal;
|
|
829
|
+
const result = await fetch(ctx.href, ctx.options);
|
|
830
|
+
return result;
|
|
831
|
+
};
|
|
832
|
+
const throwStatus = async (ctx2, api) => {
|
|
833
|
+
const response = ctx2.result;
|
|
834
|
+
if (!response.ok) {
|
|
835
|
+
api.throwError(new VigorFetchError("FETCH_FAILED", {
|
|
511
836
|
method: "request",
|
|
512
|
-
type: "fetch_error",
|
|
513
837
|
data: {
|
|
514
|
-
status:
|
|
515
|
-
|
|
516
|
-
url:
|
|
838
|
+
status: response.status,
|
|
839
|
+
response: response,
|
|
840
|
+
url: response.url,
|
|
841
|
+
headers: response.headers,
|
|
842
|
+
body: response.body,
|
|
843
|
+
statusText: response.statusText
|
|
517
844
|
}
|
|
518
845
|
}));
|
|
846
|
+
}
|
|
519
847
|
};
|
|
520
|
-
const handleBlacklist = (ctx2,
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
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
|
+
}
|
|
524
857
|
};
|
|
525
|
-
const
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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
|
+
}
|
|
532
882
|
}
|
|
533
883
|
}
|
|
534
884
|
};
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
for (const func of
|
|
543
|
-
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 });
|
|
544
894
|
}
|
|
545
|
-
ctx.
|
|
546
|
-
|
|
547
|
-
|
|
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 });
|
|
548
905
|
}
|
|
549
|
-
ctx.
|
|
550
|
-
const
|
|
551
|
-
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) {
|
|
552
908
|
await func(ctx, { setResult, throwError });
|
|
553
909
|
}
|
|
554
|
-
return ctx.
|
|
910
|
+
return ctx.result;
|
|
555
911
|
}
|
|
556
912
|
catch (error) {
|
|
557
|
-
ctx.
|
|
558
|
-
let
|
|
559
|
-
const setResult = (
|
|
560
|
-
|
|
561
|
-
|
|
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 });
|
|
562
929
|
}
|
|
563
|
-
if (
|
|
564
|
-
return
|
|
565
|
-
|
|
566
|
-
|
|
930
|
+
if (restarted) {
|
|
931
|
+
return await this.request(stats, timeline);
|
|
932
|
+
}
|
|
933
|
+
if (overwritten)
|
|
934
|
+
return ctx.result;
|
|
935
|
+
if (stats.settings.default !== VigorDefault)
|
|
936
|
+
return stats.settings.default;
|
|
567
937
|
throw error;
|
|
568
938
|
}
|
|
569
939
|
}
|
|
570
940
|
}
|
|
571
941
|
class VigorAllSettings extends VigorStatus {
|
|
572
|
-
constructor(config
|
|
573
|
-
|
|
942
|
+
constructor(config) {
|
|
943
|
+
const base = {
|
|
574
944
|
concurrency: 5,
|
|
575
|
-
jitter: 1000,
|
|
576
945
|
onlySuccess: false
|
|
577
|
-
}
|
|
946
|
+
};
|
|
947
|
+
super(config, base, (c) => new VigorAllSettings(c));
|
|
578
948
|
}
|
|
579
949
|
concurrency(num) { return this._next({ concurrency: num }); }
|
|
580
|
-
|
|
581
|
-
onlySuccess(bool) { return this._next({ onlySuccess: bool }); }
|
|
950
|
+
onlySuccess(num) { return this._next({ onlySuccess: num }); }
|
|
582
951
|
}
|
|
583
952
|
class VigorAllInterceptors extends VigorStatus {
|
|
584
|
-
constructor(config
|
|
585
|
-
|
|
953
|
+
constructor(config) {
|
|
954
|
+
const base = {
|
|
586
955
|
before: [],
|
|
587
956
|
after: [],
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
957
|
+
result: [],
|
|
958
|
+
onError: []
|
|
959
|
+
};
|
|
960
|
+
super(config, base, (c) => new VigorAllInterceptors(c));
|
|
591
961
|
}
|
|
592
|
-
before(...funcs) { return this._next({ before:
|
|
593
|
-
after(...funcs) { return this._next({ after:
|
|
594
|
-
|
|
595
|
-
|
|
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() }); }
|
|
596
966
|
}
|
|
597
967
|
class VigorAll extends VigorStatus {
|
|
598
|
-
constructor(config
|
|
599
|
-
|
|
968
|
+
constructor(config) {
|
|
969
|
+
const base = {
|
|
600
970
|
target: [],
|
|
601
|
-
|
|
602
|
-
interceptors: new VigorAllInterceptors().
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
_transfer(config) {
|
|
606
|
-
return new VigorAll({
|
|
607
|
-
...this._config,
|
|
608
|
-
...config
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
target(...args) {
|
|
612
|
-
const flat = args.flat();
|
|
613
|
-
return this._transfer({ target: flat });
|
|
971
|
+
settings: new VigorAllSettings()._getBase(),
|
|
972
|
+
interceptors: new VigorAllInterceptors()._getBase()
|
|
973
|
+
};
|
|
974
|
+
super(config, base, (c) => new VigorAll(c));
|
|
614
975
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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 });
|
|
619
982
|
}
|
|
620
983
|
interceptors(func) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
984
|
+
if (typeof func === 'function') {
|
|
985
|
+
return this._next({ interceptors: func(new VigorAllInterceptors(this._config.interceptors))._getConfig() });
|
|
986
|
+
}
|
|
987
|
+
return this._next({ interceptors: func });
|
|
624
988
|
}
|
|
625
|
-
async
|
|
626
|
-
const config = this._config;
|
|
989
|
+
async runTask(task, { stats, root }, semaphore) {
|
|
627
990
|
let ctx = {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
},
|
|
636
|
-
runtime: {
|
|
637
|
-
tasks: [],
|
|
638
|
-
result: [],
|
|
639
|
-
}
|
|
991
|
+
result: VigorDefault,
|
|
992
|
+
error: VigorDefault,
|
|
993
|
+
timeline: [],
|
|
994
|
+
stats,
|
|
995
|
+
root,
|
|
996
|
+
target: task,
|
|
997
|
+
semaphore
|
|
640
998
|
};
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
let active = 0;
|
|
647
|
-
const queue = [];
|
|
648
|
-
const runTask = async (task) => {
|
|
649
|
-
await new Promise(resolve => {
|
|
650
|
-
if (active < config.setting.concurrency) {
|
|
651
|
-
active++;
|
|
652
|
-
resolve();
|
|
653
|
-
}
|
|
654
|
-
else {
|
|
655
|
-
queue.push(() => {
|
|
656
|
-
active++;
|
|
657
|
-
resolve();
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
});
|
|
661
|
-
const throwError = (error) => { throw error; };
|
|
662
|
-
let ctxTask = {
|
|
663
|
-
target: task,
|
|
664
|
-
runtime: {
|
|
665
|
-
result: EMPTY,
|
|
666
|
-
error: null,
|
|
667
|
-
jitter: VigorRetryBackoff.randomJitter(config.setting.jitter)
|
|
668
|
-
}
|
|
669
|
-
};
|
|
999
|
+
const throwError = (err) => {
|
|
1000
|
+
ctx.timeline.push({ action: "throwError called", content: err });
|
|
1001
|
+
throw err;
|
|
1002
|
+
};
|
|
1003
|
+
try {
|
|
670
1004
|
try {
|
|
671
|
-
await
|
|
672
|
-
|
|
673
|
-
|
|
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 });
|
|
674
1010
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
for (const func of config.interceptors.after) {
|
|
678
|
-
await func(ctxTask, { setResult, throwError });
|
|
679
|
-
}
|
|
680
|
-
if (ctxTask.runtime.result === EMPTY) {
|
|
681
|
-
throw new VigorAllError("RESULT_NOT_SET", {
|
|
682
|
-
method: "request",
|
|
683
|
-
data: undefined
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
return ctxTask.runtime.result;
|
|
1011
|
+
ctx.timeline.push({ action: "task started", content: ctx.target });
|
|
1012
|
+
ctx.result = await task(ctx);
|
|
687
1013
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
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;
|
|
694
1020
|
};
|
|
695
|
-
|
|
696
|
-
|
|
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 });
|
|
697
1024
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
1025
|
+
semaphore.release();
|
|
1026
|
+
ctx.timeline.push({ action: "task released", content: ctx.target });
|
|
1027
|
+
return ctx.result;
|
|
701
1028
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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 });
|
|
707
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()
|
|
708
1055
|
};
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const isFailed = Symbol("FAILED");
|
|
712
|
-
ctx.runtime.result = settled.map((res, idx) => {
|
|
713
|
-
if (res.status === "fulfilled")
|
|
714
|
-
return res.value;
|
|
715
|
-
if (ctx.setting.onlySuccess)
|
|
716
|
-
return isFailed;
|
|
717
|
-
return new VigorAllError("REQUEST_FAILED", {
|
|
1056
|
+
if (stats.target.length === 0)
|
|
1057
|
+
throw new VigorAllError("EMPTY_TARGET", {
|
|
718
1058
|
method: "request",
|
|
719
|
-
data: {
|
|
720
|
-
index: idx,
|
|
721
|
-
error: normalizeError(res?.reason || "unknown")
|
|
722
|
-
}
|
|
1059
|
+
data: {}
|
|
723
1060
|
});
|
|
724
|
-
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
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) {
|
|
728
1099
|
await func(ctx, { setResult, throwError });
|
|
729
1100
|
}
|
|
730
|
-
return ctx.
|
|
1101
|
+
return ctx.result;
|
|
731
1102
|
}
|
|
732
1103
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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
|
-
return this.registry.VigorFetch.main().origin(origin);
|
|
765
|
-
}
|
|
766
|
-
all(...tasks) {
|
|
767
|
-
return this.registry.VigorAll
|
|
768
|
-
.main()
|
|
769
|
-
.target(...tasks);
|
|
770
|
-
}
|
|
771
|
-
parse(response) {
|
|
772
|
-
return this.registry.VigorParse.main().target(response);
|
|
773
|
-
}
|
|
774
|
-
retry(fn) {
|
|
775
|
-
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
|
|
776
1135
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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
|
+
}
|
|
797
1170
|
}
|
|
798
|
-
}
|
|
799
|
-
const vigor = new Vigor();
|
|
1171
|
+
};
|
|
800
1172
|
|
|
801
|
-
export {
|
|
1173
|
+
export { VigorEntry, vigor as default, vigor };
|