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