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