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