vigor-fetch 2.0.1 → 2.0.2

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/dist/index.js CHANGED
@@ -3,546 +3,805 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  class VigorError extends Error {
6
- constructor(text, options) {
7
- const { type, data, status, response, message, origin } = options;
8
- super(message || `[VigorError] ${text}`);
9
- this.name = this.constructor.name;
10
- this.data = data;
11
- this.type = type;
12
- this.status = status;
13
- this.response = response;
14
- this.origin = origin;
15
- if (Error.captureStackTrace) {
16
- Error.captureStackTrace(this, this.constructor);
17
- }
6
+ timestamp;
7
+ method;
8
+ cause;
9
+ context;
10
+ type;
11
+ data;
12
+ constructor(message, options) {
13
+ super(message, { cause: options?.cause });
14
+ this.name = new.target.name;
15
+ this.timestamp = new Date();
16
+ if (options?.method !== undefined)
17
+ this.method = options.method;
18
+ if (options?.context !== undefined)
19
+ this.context = options.context;
20
+ if (options?.type !== undefined)
21
+ this.type = options.type;
22
+ if (options?.data !== undefined)
23
+ this.data = options.data;
24
+ Object.setPrototypeOf(this, new.target.prototype);
25
+ Error.captureStackTrace?.(this, new.target);
18
26
  }
19
27
  }
20
28
  class VigorRetryError extends VigorError {
21
- constructor(text, options) {
22
- super(text, options);
23
- this.message = options.message || `[VigorRetryError] ${text}`;
29
+ constructor(message, options) {
30
+ super(message, options);
24
31
  }
25
32
  }
26
33
  class VigorParseError extends VigorError {
27
- constructor(text, options) {
28
- super(text, options);
29
- this.message = options.message || `[VigorParseError] ${text}`;
34
+ constructor(message, options) {
35
+ super(message, options);
30
36
  }
31
37
  }
32
38
  class VigorFetchError extends VigorError {
33
- constructor(text, options) {
34
- super(text, options);
35
- this.message = options.message || `[VigorFetchError] ${text}`;
39
+ constructor(message, options) {
40
+ super(message, options);
36
41
  }
37
42
  }
38
43
  class VigorAllError extends VigorError {
39
- constructor(text, options) {
40
- super(text, options);
41
- this.message = options.message || `[VigorAllError] ${text}`;
44
+ constructor(message, options) {
45
+ super(message, options);
42
46
  }
43
47
  }
44
- /**
45
- * VigorRetry
46
- */
47
- class VigorRetry {
48
- constructor(target, args = [], config = {}) {
49
- this._target = target;
50
- this._args = args;
51
- this._config = {
52
- retry: {
53
- count: 5, max: 10000, backoff: 1.3, baseDelay: 1000, jitter: 500
54
- },
55
- interceptors: {
56
- before: [], after: [], onRetry: [], onError: []
57
- },
58
- ...config
48
+ class VigorStatus {
49
+ _config;
50
+ _ctor;
51
+ _errorCtor;
52
+ constructor(_config, _ctor, _errorCtor) {
53
+ this._config = _config;
54
+ this._ctor = _ctor;
55
+ this._errorCtor = _errorCtor;
56
+ }
57
+ _create(config) { return this._ctor(config); }
58
+ _next(config) { return this._create({ ...this._config, ...config }); }
59
+ getConfig() { return this._config; }
60
+ _pipeSub(value, Ctor, fn, errorKey) {
61
+ const ErrorCtor = this._errorCtor?.();
62
+ if (typeof fn !== "function" && ErrorCtor) {
63
+ throw new ErrorCtor("ctor expects function", {
64
+ method: errorKey,
65
+ type: "invalid_input",
66
+ data: { expected: "function", received: fn }
67
+ });
68
+ }
69
+ return fn(new Ctor(value)).getConfig();
70
+ }
71
+ }
72
+ class VigorRetrySettings extends VigorStatus {
73
+ _base;
74
+ constructor(config) {
75
+ const base = {
76
+ count: 5,
77
+ limit: 10000,
78
+ maxDelay: 10000,
59
79
  };
80
+ super({ ...base, ...config }, (c) => new VigorRetrySettings(c));
81
+ this._base = base;
60
82
  }
61
- _next(changes) {
62
- return new this.constructor(this._target, this._args, {
63
- ...this._config,
64
- ...changes,
65
- retry: {
66
- ...this._config.retry,
67
- ...(changes.retry || {})
68
- },
69
- interceptors: {
70
- ...this._config.interceptors,
71
- ...(changes.interceptors || {})
72
- }
83
+ getBase() { return this._base; }
84
+ count(num) { return this._next({ count: num }); }
85
+ limit(num) { return this._next({ limit: num }); }
86
+ maxDelay(num) { return this._next({ maxDelay: num }); }
87
+ default(obj) { return this._next({ default: obj }); }
88
+ }
89
+ class VigorRetryBackoff extends VigorStatus {
90
+ _base;
91
+ constructor(config) {
92
+ const base = {
93
+ initialDelay: 0,
94
+ baseDelay: 1000,
95
+ factor: 1.7,
96
+ jitter: 1000
97
+ };
98
+ super({ ...base, ...config }, (c) => new VigorRetryBackoff(c));
99
+ this._base = base;
100
+ }
101
+ getBase() { return this._base; }
102
+ initialDelay(num) { return this._next({ initialDelay: num }); }
103
+ baseDelay(num) { return this._next({ baseDelay: num }); }
104
+ factor(num) { return this._next({ factor: num }); }
105
+ jitter(num) { return this._next({ jitter: num }); }
106
+ }
107
+ class VigorRetryInterceptors extends VigorStatus {
108
+ _base;
109
+ constructor(config) {
110
+ const base = {
111
+ before: [],
112
+ after: [],
113
+ onError: [],
114
+ onRetry: [],
115
+ retryIf: []
116
+ };
117
+ super({ ...base, ...config }, (c) => new VigorRetryInterceptors(c));
118
+ this._base = base;
119
+ }
120
+ getBase() { return this._base; }
121
+ before(...funcs) { return this._next({ before: [...this.getConfig().before, ...funcs.flat()] }); }
122
+ after(...funcs) { return this._next({ after: [...this.getConfig().after, ...funcs.flat()] }); }
123
+ onError(...funcs) { return this._next({ onError: [...this.getConfig().onError, ...funcs.flat()] }); }
124
+ onRetry(...funcs) { return this._next({ onRetry: [...this.getConfig().onRetry, ...funcs.flat()] }); }
125
+ retryIf(...funcs) { return this._next({ retryIf: [...this.getConfig().retryIf, ...funcs.flat()] }); }
126
+ }
127
+ class VigorRetry extends VigorStatus {
128
+ _base;
129
+ _controller = new AbortController();
130
+ constructor(config) {
131
+ const base = {
132
+ target: null,
133
+ setting: new VigorRetrySettings().getBase(),
134
+ backoff: new VigorRetryBackoff().getBase(),
135
+ interceptors: new VigorRetryInterceptors().getBase()
136
+ };
137
+ super({ ...base, ...config }, (c) => new VigorRetry(c), () => VigorRetryError);
138
+ this._base = base;
139
+ }
140
+ getBase() { return this._base; }
141
+ target(func) { return new VigorRetry({ ...this._config, target: func, setting: this._config.setting, interceptors: this._config.interceptors }); }
142
+ createController() { const controller = new AbortController(); this._controller = controller; return (error) => controller.abort(error); }
143
+ setting(func) {
144
+ return this._next({
145
+ setting: this._pipeSub(this._config.setting, VigorRetrySettings, func, "setting")
146
+ });
147
+ }
148
+ backoff(func) {
149
+ return this._next({
150
+ backoff: this._pipeSub(this._config.backoff, VigorRetryBackoff, func, "backoff")
151
+ });
152
+ }
153
+ interceptors(func) {
154
+ return this._next({
155
+ interceptors: this._pipeSub(this._config.interceptors, VigorRetryInterceptors, func, "interceptors")
73
156
  });
74
157
  }
75
- args(...args) { return new this.constructor(this._target, args, this._config); }
76
- count(int) { return this._next({ retry: { count: int } }); }
77
- max(ms) { return this._next({ retry: { max: ms } }); }
78
- backoff(ms) { return this._next({ retry: { backoff: ms } }); }
79
- baseDelay(ms) { return this._next({ retry: { baseDelay: ms } }); }
80
- jitter(ms) { return this._next({ retry: { jitter: ms } }); }
81
- before(...func) { return this._next({ interceptors: { before: [...this._config.interceptors.before, ...func.flat()] } }); }
82
- onRetry(...func) { return this._next({ interceptors: { onRetry: [...this._config.interceptors.onRetry, ...func.flat()] } }); }
83
- after(...func) { return this._next({ interceptors: { after: [...this._config.interceptors.after, ...func.flat()] } }); }
84
- onError(...func) { return this._next({ interceptors: { onError: [...this._config.interceptors.onError, ...func.flat()] } }); }
85
158
  async request() {
86
- const [target, args, config] = [this._target, this._args, this._config];
87
- const { retry: { count, max, backoff, baseDelay, jitter }, interceptors: { before, after, onRetry, onError } } = config;
88
- let ctx = { target, args, attempt: 0, result: null, error: null, try: true, retry: true, max, backoff, jitter, wait: 0, baseDelay };
159
+ const config = this._config;
160
+ let ctx = {
161
+ target: config.target,
162
+ setting: { ...config.setting },
163
+ interceptors: {
164
+ before: [...config.interceptors.before],
165
+ after: [...config.interceptors.after],
166
+ onError: [...config.interceptors.onError],
167
+ onRetry: [...config.interceptors.onRetry],
168
+ retryIf: [...config.interceptors.retryIf],
169
+ },
170
+ backoff: { ...config.backoff },
171
+ runtime: {
172
+ result: null,
173
+ controller: null,
174
+ attempt: 0,
175
+ aborted: false,
176
+ signal: null,
177
+ delay: 0,
178
+ retry: false,
179
+ }
180
+ };
181
+ const throwError = (error) => { throw error; };
182
+ const normalizeError = (obj) => {
183
+ if (obj instanceof Error) {
184
+ throw obj;
185
+ }
186
+ throw new Error(String(obj));
187
+ };
89
188
  try {
90
- if (typeof target !== 'function')
91
- throw new VigorRetryError('target is not a function', { type: "not a function", data: "target" });
92
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
93
- for (let i = 0; i < count; i++) {
94
- ctx.attempt = i + 1;
95
- ctx.error = null;
96
- ctx.result = null;
97
- ctx.retry ?? (ctx.retry = true);
98
- for (const func of before) {
99
- if (typeof func !== 'function')
100
- throw new VigorRetryError('Interceptor<before> is not a function', { type: "not a function", data: "before" });
101
- const next = await func(ctx, ctx.args);
102
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
103
- ctx = { ...ctx, ...next };
104
- }
105
- if (!ctx.try)
106
- break;
189
+ while (ctx.runtime.attempt < ctx.setting.count) {
190
+ ctx.runtime.attempt++;
191
+ ctx.runtime.controller = new AbortController();
192
+ let listener;
193
+ let timerId;
194
+ const setAttempt = (attempt) => ctx.runtime.attempt = attempt;
195
+ const abort = (error) => { if (!ctx.runtime.aborted) {
196
+ ctx.runtime.controller?.abort(error);
197
+ } };
107
198
  try {
108
- ctx.result = await ctx.target(...ctx.args);
109
- for (const func of after) {
110
- if (typeof func !== 'function')
111
- throw new VigorRetryError('Interceptor<after> is not a function', { type: "not a function", data: "after" });
112
- const next = await func(ctx, ctx.result);
113
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
114
- ctx = { ...ctx, ...next };
199
+ ctx.runtime.signal = AbortSignal.any([
200
+ this._controller.signal,
201
+ ctx.runtime.controller.signal
202
+ ]);
203
+ ctx.runtime.abortPromise = new Promise((_, reject) => {
204
+ if (ctx.runtime.signal.aborted)
205
+ reject(ctx.runtime.signal.reason);
206
+ listener = () => {
207
+ ctx.runtime.aborted = true;
208
+ reject(ctx.runtime.signal.reason);
209
+ };
210
+ ctx.runtime.signal.addEventListener("abort", listener, { once: true });
211
+ timerId = setTimeout(() => {
212
+ if (ctx.runtime.aborted)
213
+ return;
214
+ abort(new VigorRetryError(`timeouted after ${ctx.setting.limit}`, { method: "request", type: "timeout", data: { limit: ctx.setting.limit, attempt: ctx.runtime.attempt } }));
215
+ }, ctx.setting.limit);
216
+ });
217
+ for (const func of ctx.interceptors.before) {
218
+ await func(ctx, { setAttempt, throwError, abort });
219
+ if (ctx.runtime.signal.aborted)
220
+ normalizeError(ctx.runtime.signal.reason);
221
+ }
222
+ ctx.runtime.result = await Promise.race([
223
+ ctx.target(ctx, { abort, signal: ctx.runtime.signal }),
224
+ ctx.runtime.abortPromise
225
+ ]);
226
+ const setResult = (result) => ctx.runtime.result = result;
227
+ for (const func of ctx.interceptors.after) {
228
+ await func(ctx, { setAttempt, setResult, throwError });
229
+ if (ctx.runtime.signal.aborted)
230
+ normalizeError(ctx.runtime.signal.reason);
115
231
  }
116
- if (ctx.error instanceof Error)
117
- throw ctx.error;
118
- if (ctx.result instanceof Error)
119
- throw ctx.result;
120
- return ctx.result;
232
+ return ctx.runtime.result;
121
233
  }
122
234
  catch (error) {
123
- ctx.error = error;
124
- ctx.wait = Math.min(Math.pow(ctx.backoff, ctx.attempt - 1) * ctx.baseDelay, max) + ctx.jitter * Math.random();
125
- for (const func of onRetry) {
126
- if (typeof func !== 'function')
127
- throw new VigorRetryError('Interceptor<onRetry> is not a function', { type: "not a function", data: "retry" });
128
- const next = await func(ctx, ctx.error);
129
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
130
- ctx = { ...ctx, ...next };
235
+ if (ctx.runtime.aborted)
236
+ normalizeError(ctx.runtime.signal.reason);
237
+ ctx.runtime.retry = true;
238
+ ctx.runtime.error = error;
239
+ const proceedRetry = () => ctx.runtime.retry = true;
240
+ const cancelRetry = (error) => { ctx.runtime.error = error; return (ctx.runtime.retry = false); };
241
+ for (const func of ctx.interceptors.retryIf) {
242
+ await func(ctx, { throwError, proceedRetry, cancelRetry });
243
+ }
244
+ if (!ctx.runtime.retry) {
245
+ throw ctx.runtime.error;
131
246
  }
132
- if (!ctx.retry)
133
- break;
134
- await sleep(ctx.wait);
247
+ ctx.runtime.delay = Math.min(ctx.setting.maxDelay, Math.max(0, ctx.backoff.initialDelay + ctx.backoff.baseDelay * Math.pow(ctx.backoff.factor, ctx.runtime.attempt - 1))) + calculateJitter(ctx.backoff.jitter);
248
+ const setDelay = (delay) => ctx.runtime.delay = delay;
249
+ for (const func of ctx.interceptors.onRetry) {
250
+ await func(ctx, { setAttempt, throwError, setDelay });
251
+ }
252
+ await new Promise((resolve, reject) => {
253
+ const timer = setTimeout(resolve, ctx.runtime.delay);
254
+ const abortHandler = () => {
255
+ clearTimeout(timer);
256
+ reject(this._controller.signal.reason);
257
+ };
258
+ if (this._controller.signal.aborted)
259
+ return abortHandler();
260
+ this._controller.signal.addEventListener("abort", abortHandler, { once: true });
261
+ });
262
+ }
263
+ finally {
264
+ clearTimeout(timerId);
265
+ if (listener)
266
+ ctx.runtime.signal.removeEventListener("abort", listener);
135
267
  }
136
268
  }
137
- if (ctx.error instanceof Error)
138
- throw ctx.error;
139
- if (ctx.result instanceof Error)
140
- throw ctx.result;
269
+ throw new VigorRetryError(`Maximum retry attempts (${ctx.setting.count}) reached. Task failed or timed out.`, { method: "request", type: "exhausted", data: { limit: ctx.setting.limit, attempt: ctx.runtime.attempt, maxAttempts: ctx.setting.count } });
141
270
  }
142
- catch (mainError) {
143
- ctx.mainError = mainError;
144
- for (const func of onError) {
145
- if (typeof func !== 'function')
146
- throw new VigorRetryError('Interceptor<onError> is not a function', { type: "not a function", data: "onError" });
147
- const next = await func(ctx, ctx.mainError);
148
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
149
- ctx = { ...ctx, ...next };
271
+ catch (error) {
272
+ ctx.runtime.error = error;
273
+ let overrided = false;
274
+ const setResult = (result) => { overrided = true; return (ctx.runtime.result = result); };
275
+ for (const func of ctx.interceptors.onError) {
276
+ await func(ctx, { setResult, throwError });
150
277
  }
151
- if (ctx.mainError instanceof Error)
152
- throw ctx.mainError;
153
- return ctx.mainError;
278
+ if (overrided && ctx.runtime.result !== undefined)
279
+ return ctx.runtime.result;
280
+ if (ctx.setting.default !== undefined)
281
+ return ctx.setting.default;
282
+ throw error;
154
283
  }
155
- return ctx.result;
156
284
  }
157
285
  }
158
- /**
159
- * VigorParse
160
- */
161
- class VigorParse {
162
- constructor(response, config = {}) {
163
- this._response = response;
164
- this._config = {
165
- settings: { original: false, parse: null },
166
- interceptors: { before: [], after: [], onError: [] },
167
- ...config,
286
+ const basic = { key: /text/, parse: (res) => res.text(), type: "text" };
287
+ const parser = [
288
+ { key: /json/, parse: (res) => res.json(), type: "json" },
289
+ { key: /multipart\/form-data/, parse: (res) => res.formData(), type: "formData" },
290
+ { key: /octet-stream/, parse: (res) => res.arrayBuffer(), type: "arrayBuffer" },
291
+ { key: /(image|video|audio|pdf)/, parse: (res) => res.blob(), type: "blob" },
292
+ basic
293
+ ];
294
+ const supported = parser.map(i => i.type);
295
+ class VigorParse extends VigorStatus {
296
+ _base;
297
+ constructor(config) {
298
+ const base = {
299
+ original: false
168
300
  };
301
+ super({ ...base, ...config }, (c) => new VigorParse(c));
302
+ this._base = base;
169
303
  }
170
- _next(changes) {
171
- return new this.constructor(this._response, {
172
- ...this._config,
173
- ...changes,
174
- settings: {
175
- ...this._config.settings,
176
- ...(changes.settings || {})
177
- },
178
- interceptors: {
179
- ...this._config.interceptors,
180
- ...(changes.interceptors || {})
181
- }
182
- });
183
- }
184
- original(bool) { return this._next({ settings: { original: bool } }); }
185
- type(str) { return this._next({ settings: { parse: str } }); }
186
- before(...func) { return this._next({ interceptors: { before: [...this._config.interceptors.before, ...func.flat()] } }); }
187
- after(...func) { return this._next({ interceptors: { after: [...this._config.interceptors.after, ...func.flat()] } }); }
188
- onError(...func) { return this._next({ interceptors: { onError: [...this._config.interceptors.onError, ...func.flat()] } }); }
304
+ getBase() { return this._base; }
305
+ target(response) { return this._next({ target: response }); }
306
+ original(bool) { return this._next({ original: bool }); }
307
+ type(str) { return this._next({ type: str }); }
189
308
  async request() {
190
- const { settings: { original, parse }, interceptors: { before, after, onError } } = this._config;
191
- let ctx = { original, parse, result: null, response: this._response };
309
+ const config = this._config;
310
+ if (!config.target)
311
+ throw new VigorParseError("target is required", { method: "request", type: "invalid_target", data: {
312
+ expected: "Response",
313
+ received: config.target,
314
+ } });
315
+ if (config.original)
316
+ return config.target;
317
+ const contentType = config.target.headers.get("Content-Type") || "";
318
+ let strategy;
192
319
  try {
193
- for (const func of before) {
194
- if (typeof func !== 'function')
195
- throw new VigorParseError('Interceptor<before> is not a function', { type: "not a function", data: "before" });
196
- const next = await func(ctx, ctx.response);
197
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
198
- ctx = { ...ctx, ...next };
199
- }
200
- ctx.result = await (async (response) => {
201
- if (ctx.original)
202
- return response;
203
- if (ctx.parse) {
204
- const method = response[ctx.parse];
205
- if (!method || typeof method !== 'function')
206
- throw new VigorParseError(`Invalid method such as ${ctx.parse}`, { type: "Invalid method", data: ctx.parse });
207
- return await method.call(response);
208
- }
209
- const contentType = response.headers.get("Content-Type") || "";
210
- if (/json/.test(contentType))
211
- return await response.json();
212
- if (/multipart\/form-data/.test(contentType))
213
- return await response.formData();
214
- if (/octet-stream/.test(contentType))
215
- return await response.arrayBuffer();
216
- if (/(image|video|audio|pdf)/.test(contentType))
217
- return await response.blob();
218
- return await response.text();
219
- })(ctx.response);
220
- for (const func of after) {
221
- if (typeof func !== 'function')
222
- throw new VigorParseError('Interceptor<after> is not a function', { type: "not a function", data: "after" });
223
- const next = await func(ctx, ctx.result);
224
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
225
- ctx = { ...ctx, ...next };
320
+ if (config.type) {
321
+ strategy = { type: config.type };
322
+ const parser = config.target[config.type];
323
+ if (!parser || typeof parser !== 'function')
324
+ throw new VigorParseError(`failed to parse: '${strategy?.type ?? "unknown"}'`, { method: "request", type: "invalid_type", data: {
325
+ expected: config.type,
326
+ supported: supported,
327
+ response: config.target,
328
+ headers: contentType,
329
+ } });
330
+ return await parser();
226
331
  }
227
- if (ctx.result instanceof Error)
228
- throw ctx.result;
229
- return ctx.result;
332
+ strategy = parser.find(i => i.key.test(contentType)) ?? basic;
333
+ return await strategy.parse(config.target);
230
334
  }
231
- catch (mainError) {
232
- ctx.mainError = mainError;
233
- for (const func of onError) {
234
- if (typeof func !== 'function')
235
- throw new VigorParseError('Interceptor<onError> is not a function', { type: "not a function", data: "onError" });
236
- const next = await func(ctx, ctx.mainError);
237
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
238
- ctx = { ...ctx, ...next };
239
- }
240
- if (ctx.mainError instanceof Error)
241
- throw ctx.mainError;
242
- return ctx.mainError;
335
+ catch (error) {
336
+ if (error instanceof VigorParseError)
337
+ throw error;
338
+ throw new VigorParseError(`failed to parse: '${strategy?.type ?? "unknown"}'`, { method: "request", type: "parse_failed", data: {
339
+ expected: strategy?.type ?? "unknown",
340
+ supported: supported,
341
+ response: config.target,
342
+ headers: contentType,
343
+ error
344
+ } });
243
345
  }
244
346
  }
245
347
  }
246
- /**
247
- * VigorFetch
248
- */
249
- class VigorFetch {
250
- constructor(origin = "", config = {}) {
251
- this._config = {
252
- request: { origin, path: "", query: {}, method: "", headers: {}, body: null, offset: {} },
253
- retry: { limit: 10000, retryHeaders: ["retry-after", "ratelimit-reset"], unretry: new Set([400, 404]) },
254
- response: { retryConfig: undefined, parseConfig: undefined },
255
- interceptors: { before: [], after: [], onError: [], result: [] },
256
- ...config
348
+ class VigorFetchSettings extends VigorStatus {
349
+ _base;
350
+ constructor(config) {
351
+ const base = {
352
+ origin: "",
353
+ path: [],
354
+ query: {},
355
+ unretry: [400, 401, 403, 404, 405, 413, 422],
356
+ retryHeaders: ["retry-after", "ratelimit-reset", "x-ratelimit-reset", "x-retry-after", "x-amz-retry-after", "chrome-proxy-next-link"],
257
357
  };
358
+ super({ ...base, ...config }, (c) => new VigorFetchSettings(c));
359
+ this._base = base;
258
360
  }
259
- _next(changes) {
260
- const newConfig = {
261
- ...this._config,
262
- request: { ...this._config.request, ...(changes.request || {}) },
263
- retry: { ...this._config.retry, ...(changes.retry || {}) },
264
- response: { ...this._config.response, ...(changes.response || {}) },
265
- interceptors: {
266
- before: [...(this._config.interceptors.before || [])],
267
- after: [...(this._config.interceptors.after || [])],
268
- onError: [...(this._config.interceptors.onError || [])],
269
- result: [...(this._config.interceptors.result || [])],
270
- ...(changes.interceptors || {})
271
- }
361
+ getBase() { return this._base; }
362
+ origin(str) { return this._next({ origin: str }); }
363
+ path(...strs) { return this._next({ path: [...this._config.path, ...strs.flat()] }); }
364
+ query(obj) { return this._next({ query: { ...this._config.query, ...obj } }); }
365
+ unretry(...numbers) { return this._next({ unretry: numbers.flat() }); }
366
+ retryHeaders(...strs) { return this._next({ retryHeaders: [...this._config.retryHeaders, ...strs.flat()] }); }
367
+ method(str) { return this._next({ method: str }); }
368
+ headers(obj) { return this._next({ headers: obj }); }
369
+ body(obj) { return this._next({ body: obj }); }
370
+ options(obj) { return this._next({ options: obj }); }
371
+ default(obj) { return this._next({ default: obj }); }
372
+ }
373
+ class VigorFetchInterceptors extends VigorStatus {
374
+ _base;
375
+ constructor(config) {
376
+ const base = {
377
+ before: [],
378
+ after: [],
379
+ onError: [],
380
+ result: []
272
381
  };
273
- if (changes.interceptors) {
274
- Object.keys(changes.interceptors).forEach(key => {
275
- if (Array.isArray(changes.interceptors[key])) {
276
- newConfig.interceptors[key] = [
277
- ...this._config.interceptors[key],
278
- ...changes.interceptors[key]
279
- ];
382
+ super({ ...base, ...config }, (c) => new VigorFetchInterceptors(c));
383
+ this._base = base;
384
+ }
385
+ getBase() { return this._base; }
386
+ before(...funcs) { return this._next({ before: [...this.getConfig().before, ...funcs.flat()] }); }
387
+ after(...funcs) { return this._next({ after: [...this.getConfig().after, ...funcs.flat()] }); }
388
+ onError(...funcs) { return this._next({ onError: [...this.getConfig().onError, ...funcs.flat()] }); }
389
+ result(...funcs) { return this._next({ result: [...this.getConfig().result, ...funcs.flat()] }); }
390
+ }
391
+ class VigorFetch extends VigorStatus {
392
+ _base;
393
+ constructor(config) {
394
+ const base = {
395
+ setting: new VigorFetchSettings().getBase(),
396
+ retryConfig: new VigorRetry().getBase(),
397
+ parseConfig: new VigorParse().getBase(),
398
+ interceptors: new VigorFetchInterceptors().getBase(),
399
+ };
400
+ super({ ...base, ...config }, (c) => new VigorFetch(c), () => VigorRetryError);
401
+ this._base = base;
402
+ }
403
+ getBase() { return this._base; }
404
+ origin(str) { return this._next({ setting: { ...this._config.setting, origin: str } }); }
405
+ path(...strs) { return this._next({ setting: { ...this._config.setting, path: [...this._config.setting.path, ...strs.flat()] } }); }
406
+ query(obj) { return this._next({ setting: { ...this._config.setting, query: { ...this._config.setting.query, ...obj } } }); }
407
+ method(str) { return this._next({ setting: { ...this._config.setting, method: str } }); }
408
+ headers(obj) { return this._next({ setting: { ...this._config.setting, headers: obj } }); }
409
+ body(obj) { return this._next({ setting: { ...this._config.setting, body: obj } }); }
410
+ options(obj) { return this._next({ setting: { ...this._config.setting, options: obj } }); }
411
+ setting(func) {
412
+ return this._next({
413
+ setting: this._pipeSub(this._config.setting, VigorFetchSettings, func, "setting")
414
+ });
415
+ }
416
+ retryConfig(func) {
417
+ return this._next({
418
+ retryConfig: this._pipeSub(this._config.retryConfig, VigorRetry, func, "retryConfig")
419
+ });
420
+ }
421
+ parseConfig(func) {
422
+ return this._next({
423
+ parseConfig: this._pipeSub(this._config.parseConfig, VigorParse, func, "parseConfig")
424
+ });
425
+ }
426
+ buildUrl(origin, path, query) {
427
+ if (!origin)
428
+ throw new VigorFetchError("buildUrl expects 'origin'", {
429
+ type: "invalid_input", method: "buildUrl", data: {
430
+ expected: "string", received: origin
431
+ }
432
+ });
433
+ try {
434
+ const url = new URL(origin);
435
+ if (path && path.length > 0) {
436
+ const cleanPath = path
437
+ .filter(p => p && typeof p === 'string')
438
+ .map(p => p.replace(/^\/+|\/+$/g, ''))
439
+ .join('/');
440
+ if (cleanPath) {
441
+ const base = url.pathname.endsWith('/') ? url.pathname : url.pathname + '/';
442
+ url.pathname = base + cleanPath;
280
443
  }
444
+ }
445
+ if (query && typeof query === 'object') {
446
+ Object.entries(query).forEach(([key, value]) => {
447
+ if (value === null || value === undefined)
448
+ return;
449
+ if (Array.isArray(value)) {
450
+ value.forEach(v => url.searchParams.append(key, String(v)));
451
+ }
452
+ else {
453
+ url.searchParams.set(key, String(value));
454
+ }
455
+ });
456
+ }
457
+ return url.toString();
458
+ }
459
+ catch (e) {
460
+ throw new VigorFetchError(`Invalid URL origin: ${origin}`, {
461
+ type: "invalid_url", method: "buildUrl", data: { error: e }
281
462
  });
282
463
  }
283
- return new this.constructor(newConfig.request.origin, newConfig);
284
- }
285
- origin(str) { return this._next({ request: { origin: str } }); }
286
- path(str) { return this._next({ request: { path: str } }); }
287
- query(obj) { return this._next({ request: { query: obj } }); }
288
- method(str) { return this._next({ request: { method: str } }); }
289
- headers(obj) { return this._next({ request: { headers: obj } }); }
290
- body(obj) { return this._next({ request: { body: obj } }); }
291
- offset(obj) { return this._next({ request: { offset: obj } }); }
292
- limit(ms) { return this._next({ retry: { limit: ms } }); }
293
- retryHeaders(...str) { return this._next({ retry: { retryHeaders: [...this._config.retry.retryHeaders, ...str.flat()] } }); }
294
- unretry(...int) { return this._next({ retry: { unretry: new Set(int.flat()) } }); }
295
- before(...func) { return this._next({ interceptors: { before: [...this._config.interceptors.before, ...func.flat()] } }); }
296
- after(...func) { return this._next({ interceptors: { after: [...this._config.interceptors.after, ...func.flat()] } }); }
297
- result(...func) { return this._next({ interceptors: { result: [...this._config.interceptors.result, ...func.flat()] } }); }
298
- onError(...func) { return this._next({ interceptors: { onError: [...this._config.interceptors.onError, ...func.flat()] } }); }
299
- retryConfig(func) {
300
- if (typeof func !== 'function')
301
- throw new VigorFetchError("retryConfig is not a function", { type: "not a function", data: "retryConfig" });
302
- const dummyRetry = func(new VigorRetry(async () => { }));
303
- return this._next({ retry: { retryConfig: dummyRetry['_config'] } });
304
464
  }
305
- parseConfig(func) {
306
- if (typeof func !== 'function')
307
- throw new VigorFetchError("parseConfig is not a function", { type: "not a function", data: "parseConfig" });
308
- const dummyParse = func(new VigorParse(null));
309
- return this._next({ response: { parseConfig: dummyParse['_config'] } });
465
+ interceptors(func) {
466
+ return this._next({
467
+ interceptors: this._pipeSub(this._config.interceptors, VigorFetchInterceptors, func, "interceptors")
468
+ });
310
469
  }
311
470
  async request() {
312
- const { request: { origin, path, query, method, headers, body, offset }, retry: { limit, retryHeaders, unretry }, interceptors: { before, after, onError, result }, response: { retryConfig, parseConfig } } = this._config;
313
- let ctx = { option: null, result: null, path, origin };
471
+ const config = this._config;
472
+ let ctx = {
473
+ setting: { ...config.setting },
474
+ retryConfig: {
475
+ ...config.retryConfig,
476
+ interceptors: {
477
+ before: [...config.retryConfig.interceptors.before],
478
+ after: [...config.retryConfig.interceptors.after],
479
+ onError: [...config.retryConfig.interceptors.onError],
480
+ onRetry: [...config.retryConfig.interceptors.onRetry],
481
+ retryIf: [...config.retryConfig.interceptors.retryIf],
482
+ }
483
+ },
484
+ parseConfig: {
485
+ ...config.parseConfig
486
+ },
487
+ interceptors: {
488
+ before: [...config.interceptors.before],
489
+ after: [...config.interceptors.after],
490
+ onError: [...config.interceptors.onError],
491
+ result: [...config.interceptors.result]
492
+ },
493
+ runtime: {}
494
+ };
495
+ const throwError = (error) => { throw error; };
314
496
  try {
315
- if (!/^(https?|data|blob|file|about):\/\//.test(origin))
316
- throw new VigorFetchError(`${origin} Invalid Protocol`, { type: "Invalid Protocol", data: origin, origin: origin, status: 0 });
317
- const isJson = Array.isArray(body) || (!!body && Object.getPrototypeOf(body) === Object.prototype);
318
- ctx.option = {
319
- method: method || (body ? "POST" : "GET"),
320
- headers: { ...(isJson && { "Content-Type": "application/json" }), ...headers },
321
- ...(body && { body: isJson ? JSON.stringify(body) : body }),
322
- ...offset
497
+ ctx.runtime.unretrySet = new Set(ctx.setting.unretry);
498
+ if (!/^(https?|data|blob|file|about):\/\//.test(ctx.setting.origin))
499
+ throw new VigorFetchError(`Invalid Protocol`, { type: "Invalid Protocol", method: "request", data: {
500
+ expected: ["http", "https", "data", "blob", "file", "about"], received: ctx.setting.origin
501
+ } });
502
+ ctx.runtime.url = this.buildUrl(config.setting.origin, config.setting.path, config.setting.query);
503
+ const isJson = Array.isArray(ctx.setting.body) || (!!ctx.setting.body && Object.getPrototypeOf(ctx.setting.body) === Object.prototype);
504
+ ctx.runtime.baseOptions = {
505
+ method: ctx.setting.method || (ctx.setting.body ? "POST" : "GET"),
506
+ headers: { ...(isJson && { "Content-Type": "application/json" }), ...ctx.setting.headers },
507
+ ...(ctx.setting.body && { body: isJson ? JSON.stringify(ctx.setting.body) : ctx.setting.body }),
508
+ ...ctx.setting.options,
509
+ signal: null
323
510
  };
324
- for (const func of before) {
325
- if (typeof func !== 'function')
326
- throw new VigorFetchError('Interceptor<before> is not a function', { type: "not a function", data: "before" });
327
- const next = await func(ctx, ctx.option);
328
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
329
- ctx = { ...ctx, ...next };
330
- }
331
- const originBase = ctx.origin.endsWith('/') ? ctx.origin : ctx.origin + '/';
332
- const cleanPath = ctx.path.replace(/^\//, "");
333
- const urlObj = cleanPath ? new URL(cleanPath, originBase) : new URL(ctx.origin);
334
- Object.entries(query).forEach(([key, value]) => {
335
- if (value !== null && value !== undefined)
336
- urlObj.searchParams.append(key, String(value));
337
- });
338
- const url = urlObj.href;
339
- ctx.url = url;
340
- const fetchTarget = async () => {
341
- const controller = new AbortController();
342
- const abort = setTimeout(() => controller.abort(), limit);
343
- const http = ctx.option;
344
- http.signal = controller.signal;
345
- const res = await fetch(url, http);
346
- clearTimeout(abort);
347
- return res;
511
+ const target = async (ctx2, { signal }) => {
512
+ ctx.runtime.options = {
513
+ ...ctx.runtime.baseOptions,
514
+ signal
515
+ };
516
+ const response = await fetch(ctx.runtime.url, ctx.runtime.options);
517
+ return response;
348
518
  };
349
- const handle429 = async (ctx) => {
350
- const res = ctx.result;
351
- if (unretry.has(res.status))
352
- throw new Error(`Unretry ${res.status}`);
353
- if (!res || res.status !== 429)
354
- return;
355
- const rHeader = retryHeaders.map((h) => res.headers.get(h)).find(Boolean);
356
- let delay = 0;
357
- if (rHeader) {
358
- delay = isNaN(Number(rHeader)) ? new Date(rHeader).getTime() - Date.now() : Number(rHeader) * 1000;
519
+ const checkOk = async (ctx2, { throwError }) => {
520
+ const result = ctx2.runtime.result;
521
+ if (!result.ok)
522
+ return throwError?.(new VigorFetchError(`HTTP Error: ${result.status} ${result.statusText}`, {
523
+ method: "request", type: "fetch_error",
524
+ data: { status: result.status, statusText: result.statusText, url: result.url }
525
+ }));
526
+ };
527
+ const handleBlacklist = (ctx2, { cancelRetry }) => {
528
+ const result = ctx2.runtime.result;
529
+ if (!result?.status || ctx.runtime.unretrySet.has(result.status))
530
+ cancelRetry?.();
531
+ };
532
+ const handle429 = (ctx2, { setDelay }) => {
533
+ const result = ctx2.runtime.result;
534
+ if (result?.status === 429) {
535
+ let rHeader = null;
536
+ ctx.setting.retryHeaders.some(h => (rHeader = result.headers.get(h)));
537
+ if (rHeader) {
538
+ setDelay?.(isNaN(Number(rHeader)) ? new Date(rHeader).getTime() - Date.now() : Number(rHeader) * 1000);
539
+ }
359
540
  }
360
- ctx.wait = Math.max(0, delay) + Math.random() * ctx.jitter;
361
- if (ctx.wait > ctx.max)
362
- throw new Error(`${url} Timeouted ${ctx.wait}ms`);
363
- await new Promise(r => setTimeout(r, ctx.wait));
364
- ctx.retry = true;
365
541
  };
366
- const retryInstance = new VigorRetry(fetchTarget, [], retryConfig).onRetry(handle429);
367
- ctx.result = await retryInstance.request();
368
- for (const func of after) {
369
- if (typeof func !== 'function')
370
- throw new VigorFetchError('Interceptor<after> is not a function', { type: "not a function", data: "after" });
371
- const next = await func(ctx, ctx.result);
372
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
373
- ctx = { ...ctx, ...next };
542
+ ctx.retryConfig.target = target;
543
+ ctx.retryConfig.interceptors.after.unshift(checkOk);
544
+ ctx.retryConfig.interceptors.retryIf.unshift(handleBlacklist);
545
+ ctx.retryConfig.interceptors.onRetry.unshift(handle429);
546
+ ctx.runtime.retryEngine = new VigorRetry(ctx.retryConfig);
547
+ ctx.runtime.parseEngine = new VigorParse(ctx.parseConfig);
548
+ const setOptions = (obj) => ctx.runtime.baseOptions = obj;
549
+ for (const func of ctx.interceptors.before) {
550
+ await func(ctx, { setOptions, throwError });
374
551
  }
375
- const parseInstance = new VigorParse(ctx.result, parseConfig);
376
- ctx.final = await parseInstance.request();
377
- const finalInterceptors = this._config.interceptors.result || [];
378
- for (const func of finalInterceptors) {
379
- if (typeof func !== 'function')
380
- continue;
381
- const next = await func(ctx.final);
382
- if (next !== undefined)
383
- ctx.final = next;
552
+ ctx.runtime.response = await ctx.runtime.retryEngine.request();
553
+ for (const func of ctx.interceptors.after) {
554
+ await func(ctx, { throwError });
384
555
  }
385
- if (ctx.final instanceof Error)
386
- throw ctx.final;
387
- return ctx.final;
556
+ ctx.runtime.result = await ctx.runtime.parseEngine?.target(ctx.runtime.response).request();
557
+ const setResult = (result) => ctx.runtime.result = result;
558
+ for (const func of ctx.interceptors.result) {
559
+ await func(ctx, { setResult, throwError });
560
+ }
561
+ return ctx.runtime.result;
388
562
  }
389
- catch (mainError) {
390
- ctx.mainError = mainError;
391
- for (const func of onError) {
392
- if (typeof func !== 'function')
393
- throw new VigorFetchError('Interceptor<onError> is not a function', { type: "not a function", data: "onError" });
394
- const next = await func(ctx, ctx.mainError);
395
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
396
- ctx = { ...ctx, ...next };
563
+ catch (error) {
564
+ ctx.runtime.error = error;
565
+ let overrided = false;
566
+ const setResult = (result) => { overrided = true; return (ctx.runtime.result = result); };
567
+ for (const func of ctx.interceptors.onError) {
568
+ await func(ctx, { setResult, throwError });
397
569
  }
398
- if (ctx.mainError instanceof Error)
399
- throw ctx.mainError;
400
- return ctx.mainError;
570
+ if (overrided && ctx.runtime.result !== undefined)
571
+ return ctx.runtime.result;
572
+ if (ctx.setting.default !== undefined)
573
+ return ctx.setting.default;
574
+ throw error;
401
575
  }
402
576
  }
403
577
  }
404
- /**
405
- * VigorAll
406
- */
407
- class VigorAll {
578
+ class VigorAllSettings extends VigorStatus {
579
+ _base;
408
580
  constructor(config) {
409
- this._config = {
410
- settings: { limit: 10, jitter: 1000 },
411
- request: { promises: [] },
412
- response: { retryConfig: undefined, parseConfig: undefined },
413
- interceptors: { before: [], after: [], onError: [] },
414
- ...config
581
+ const base = {
582
+ concurrency: 5,
583
+ jitter: 1000
415
584
  };
585
+ super({ ...base, ...config }, (c) => new VigorAllSettings(c));
586
+ this._base = base;
416
587
  }
417
- _next(changes) {
418
- return new this.constructor({
419
- ...this._config,
420
- ...changes,
421
- settings: {
422
- ...this._config.settings,
423
- ...(changes.settings || {})
424
- },
425
- request: {
426
- ...this._config.request,
427
- ...(changes.request || {})
428
- },
429
- interceptors: {
430
- ...this._config.interceptors,
431
- ...(changes.interceptors || {})
432
- }
588
+ getBase() { return this._base; }
589
+ concurrency(num) { return this._next({ concurrency: num }); }
590
+ jitter(num) { return this._next({ jitter: num }); }
591
+ }
592
+ class VigorAllInterceptors extends VigorStatus {
593
+ _base;
594
+ constructor(config) {
595
+ const base = {
596
+ before: [],
597
+ after: [],
598
+ onError: [],
599
+ result: []
600
+ };
601
+ super({ ...base, ...config }, (c) => new VigorAllInterceptors(c));
602
+ this._base = base;
603
+ }
604
+ getBase() { return this._base; }
605
+ before(...funcs) { return this._next({ before: [...this.getConfig().before, ...funcs.flat()] }); }
606
+ after(...funcs) { return this._next({ after: [...this.getConfig().after, ...funcs.flat()] }); }
607
+ onError(...funcs) { return this._next({ onError: [...this.getConfig().onError, ...funcs.flat()] }); }
608
+ result(...funcs) { return this._next({ result: [...this.getConfig().result, ...funcs.flat()] }); }
609
+ }
610
+ class VigorAll extends VigorStatus {
611
+ _base;
612
+ constructor(config) {
613
+ const base = {
614
+ target: [],
615
+ setting: new VigorAllSettings().getBase(),
616
+ interceptors: new VigorAllInterceptors().getBase()
617
+ };
618
+ super({ ...base, ...config }, (c) => new VigorAll(c), () => VigorAllError);
619
+ this._base = base;
620
+ }
621
+ getBase() { return this._base; }
622
+ target(...funcs) { return this._next({ target: [...this._config.target, ...funcs.flat()] }); }
623
+ setting(func) {
624
+ return this._next({
625
+ setting: this._pipeSub(this._config.setting, VigorAllSettings, func, "setting")
626
+ });
627
+ }
628
+ interceptors(func) {
629
+ return this._next({
630
+ interceptors: this._pipeSub(this._config.interceptors, VigorAllInterceptors, func, "interceptors")
433
631
  });
434
632
  }
435
- promises(...func) { return this._next({ request: { promises: [...this._config.request.promises, ...func.flat()] } }); }
436
- limit(int) { return this._next({ settings: { limit: int } }); }
437
- jitter(ms) { return this._next({ settings: { jitter: ms } }); }
438
- before(...func) { return this._next({ interceptors: { before: [...this._config.interceptors.before, ...func.flat()] } }); }
439
- after(...func) { return this._next({ interceptors: { after: [...this._config.interceptors.after, ...func.flat()] } }); }
440
- onError(...func) { return this._next({ interceptors: { onError: [...this._config.interceptors.onError, ...func.flat()] } }); }
441
633
  async request() {
442
- const { settings: { limit, jitter }, request: { promises }, interceptors: { before, after, onError } } = this._config;
443
- let ctx = { limit, jitter, promises, result: null };
444
- try {
445
- for (const func of before || []) {
446
- if (typeof func !== 'function')
447
- throw new VigorAllError('Interceptor<before> is not a function', { type: "not a function", data: "before" });
448
- const next = await func(ctx, ctx.promises);
449
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
450
- ctx = { ...ctx, ...next };
634
+ const config = this._config;
635
+ let ctx = {
636
+ target: [...config.target],
637
+ setting: { ...config.setting },
638
+ interceptors: {
639
+ before: [...config.interceptors.before],
640
+ after: [...config.interceptors.after],
641
+ onError: [...config.interceptors.onError],
642
+ result: [...config.interceptors.result]
643
+ },
644
+ runtime: {
645
+ tasks: [],
646
+ result: []
451
647
  }
452
- const results = [];
453
- const executing = new Set();
454
- for (const task of ctx.promises) {
455
- const p = Promise.resolve()
456
- .then(() => new Promise(res => setTimeout(res, Math.random() * ctx.jitter)))
457
- .then(() => task());
458
- results.push(p);
459
- executing.add(p);
460
- p.finally(() => executing.delete(p));
461
- if (executing.size >= ctx.limit) {
462
- await Promise.race(executing);
648
+ };
649
+ if (ctx.target?.length == 0)
650
+ throw new VigorFetchError("request expects 'target'", {
651
+ type: "invalid_input", method: "request", data: {
652
+ expected: "string", received: ctx.target
653
+ }
654
+ });
655
+ let active = 0;
656
+ const queue = [];
657
+ const runTask = async (task) => {
658
+ await new Promise(resolve => {
659
+ if (active < ctx.setting.concurrency) {
660
+ active++;
661
+ resolve();
662
+ }
663
+ else {
664
+ queue.push(() => {
665
+ active++;
666
+ resolve();
667
+ });
463
668
  }
464
- }
465
- const ready = await Promise.allSettled(results);
466
- ctx.result = ready.map(i => {
467
- if (i.status === "fulfilled")
468
- return i.value;
469
- return i.reason instanceof VigorAllError ? i.reason : new VigorAllError(i.reason?.message || "Unknown", { message: i.reason?.message || "Unknown" });
470
669
  });
471
- for (const func of after) {
472
- if (typeof func !== 'function')
473
- throw new VigorAllError('Interceptor<after> is not a function', { type: "not a function", data: "after" });
474
- const next = await func(ctx, ctx.result);
475
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
476
- ctx = { ...ctx, ...next };
670
+ const throwError = (error) => { throw error; };
671
+ try {
672
+ await new Promise(resolve => setTimeout(resolve, ctx.setting.jitter));
673
+ let res;
674
+ for (const func of ctx.interceptors.before) {
675
+ await func(ctx, { throwError });
676
+ }
677
+ res = await task(ctx, {});
678
+ const setResult = (result) => res = result;
679
+ for (const func of ctx.interceptors.after) {
680
+ await func(ctx, { setResult, throwError });
681
+ }
682
+ return res;
477
683
  }
478
- if (ctx.result instanceof Error)
479
- throw ctx.result;
480
- return ctx.result;
481
- }
482
- catch (mainError) {
483
- ctx.mainError = mainError;
484
- for (const func of onError) {
485
- if (typeof func !== 'function')
486
- throw new VigorAllError('Interceptor<onError> is not a function', { type: "not a function", data: "onError" });
487
- const next = await func(ctx, ctx.mainError);
488
- if (next !== undefined && typeof next === 'object' && !Array.isArray(next))
489
- ctx = { ...ctx, ...next };
684
+ catch (error) {
685
+ let res;
686
+ let overrided = false;
687
+ const setResult = (result) => { overrided = true; return (res = result); };
688
+ for (const func of ctx.interceptors.onError) {
689
+ await func(ctx, { setResult, throwError });
690
+ }
691
+ if (overrided && res !== undefined)
692
+ return res;
693
+ throw error;
694
+ }
695
+ finally {
696
+ active--;
697
+ const next = queue.shift();
698
+ if (next)
699
+ next();
490
700
  }
491
- if (ctx.mainError instanceof Error)
492
- throw ctx.mainError;
493
- return ctx.mainError;
701
+ };
702
+ ctx.runtime.tasks = ctx.target.map(task => runTask(task));
703
+ const settled = await Promise.allSettled(ctx.runtime.tasks);
704
+ ctx.runtime.result = settled.map(i => {
705
+ if (i.status === "fulfilled")
706
+ return i.value;
707
+ return new VigorAllError(`this request failed`, {
708
+ method: "request", type: "request_failed", data: {
709
+ error: i.reason
710
+ }
711
+ });
712
+ });
713
+ const setResult = (result) => ctx.runtime.result = result;
714
+ const throwError = (error) => { throw error; };
715
+ for (const func of ctx.interceptors.result) {
716
+ await func(ctx, { setResult, throwError });
494
717
  }
718
+ return ctx.runtime.result;
495
719
  }
496
720
  }
497
- /**
498
- * Main Vigor Class
499
- */
721
+ function calculateJitter(jitter) {
722
+ return jitter * (Math.random() * 2 - 1);
723
+ }
500
724
  class Vigor {
501
- constructor() {
502
- this._Fetch = VigorFetch;
503
- this._Retry = VigorRetry;
504
- this._Parse = VigorParse;
505
- this._All = VigorAll;
506
- }
507
- use(plugin, options = {}) {
508
- if (typeof plugin === 'function') {
509
- plugin(this, options);
510
- }
511
- return this;
725
+ registry;
726
+ constructor(config) {
727
+ const defaultRegistry = {
728
+ VigorRetry: {
729
+ main: () => new VigorRetry(),
730
+ error: VigorRetryError,
731
+ setting: VigorRetrySettings,
732
+ interceptors: VigorRetryInterceptors,
733
+ backoff: VigorRetryBackoff,
734
+ },
735
+ VigorFetch: {
736
+ main: () => new VigorFetch(),
737
+ error: VigorFetchError,
738
+ setting: VigorFetchSettings,
739
+ interceptors: VigorFetchInterceptors,
740
+ },
741
+ VigorAll: {
742
+ main: () => new VigorAll(),
743
+ error: VigorAllError,
744
+ setting: VigorAllSettings,
745
+ interceptors: VigorAllInterceptors,
746
+ },
747
+ VigorParse: {
748
+ main: () => new VigorParse(),
749
+ error: VigorParseError,
750
+ }
751
+ };
752
+ this.registry = config?.registry ?? defaultRegistry;
753
+ }
754
+ fetch(origin) {
755
+ return this.registry.VigorFetch.main().origin(origin);
512
756
  }
513
- fetch(origin, config) {
514
- return new this._Fetch(origin, config);
757
+ all(tasks) {
758
+ return this.registry.VigorAll.main().target(tasks.flat());
515
759
  }
516
- retry(target, args, config) {
517
- return new this._Retry(target, args, config);
760
+ parse(response) {
761
+ return this.registry.VigorParse.main().target(response);
518
762
  }
519
- parse(response, config) {
520
- return new this._Parse(response, config);
763
+ retry(fn) {
764
+ return this.registry.VigorRetry.main().target(fn);
521
765
  }
522
- all(config) {
523
- return new this._All(config);
766
+ use(plugin, options) {
767
+ const nextRegistry = {
768
+ ...this.registry,
769
+ VigorFetch: {
770
+ ...this.registry.VigorFetch
771
+ },
772
+ VigorRetry: {
773
+ ...this.registry.VigorRetry
774
+ },
775
+ VigorAll: {
776
+ ...this.registry.VigorAll
777
+ },
778
+ VigorParse: {
779
+ ...this.registry.VigorParse
780
+ }
781
+ };
782
+ plugin(nextRegistry, options);
783
+ return new Vigor({
784
+ registry: nextRegistry
785
+ });
524
786
  }
525
787
  }
526
788
  const vigor = new Vigor();
527
- const vigorInstance = vigor;
528
- vigorInstance.VigorError = VigorError;
529
- vigorInstance.VigorRetryError = VigorRetryError;
530
- vigorInstance.VigorParseError = VigorParseError;
531
- vigorInstance.VigorFetchError = VigorFetchError;
532
- vigorInstance.VigorAllError = VigorAllError;
533
- vigorInstance.VigorFetch = VigorFetch;
534
- vigorInstance.VigorRetry = VigorRetry;
535
- vigorInstance.VigorParse = VigorParse;
536
- vigorInstance.VigorAll = VigorAll;
537
789
 
790
+ exports.Vigor = Vigor;
538
791
  exports.VigorAll = VigorAll;
539
792
  exports.VigorAllError = VigorAllError;
540
- exports.VigorError = VigorError;
793
+ exports.VigorAllInterceptors = VigorAllInterceptors;
794
+ exports.VigorAllSettings = VigorAllSettings;
541
795
  exports.VigorFetch = VigorFetch;
542
796
  exports.VigorFetchError = VigorFetchError;
797
+ exports.VigorFetchInterceptors = VigorFetchInterceptors;
798
+ exports.VigorFetchSettings = VigorFetchSettings;
543
799
  exports.VigorParse = VigorParse;
544
800
  exports.VigorParseError = VigorParseError;
545
801
  exports.VigorRetry = VigorRetry;
802
+ exports.VigorRetryBackoff = VigorRetryBackoff;
546
803
  exports.VigorRetryError = VigorRetryError;
804
+ exports.VigorRetryInterceptors = VigorRetryInterceptors;
805
+ exports.VigorRetrySettings = VigorRetrySettings;
547
806
  exports.default = vigor;
548
807
  exports.vigor = vigor;