vigor-fetch 3.1.9 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/README.md +471 -1143
- package/dist/index.d.ts +5717 -742
- package/dist/index.js +1726 -1530
- package/dist/index.mjs +1678 -1530
- package/package.json +56 -52
package/dist/index.js
CHANGED
|
@@ -2,791 +2,1357 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Builds the literal brand string used by {@link VigorBrand}, preserving the
|
|
7
|
+
* `Branch`/`Name` literal types for use as a `__brand` value.
|
|
8
|
+
*/
|
|
9
|
+
function createBrand(b, n) {
|
|
10
|
+
return `@vigor-fetch:::${b} <- ${n}`;
|
|
11
|
+
}
|
|
12
|
+
const VigorDefault = Symbol("VigorDefault");
|
|
13
|
+
/** Type guard checking whether a value is the {@link VigorDefault} sentinel. */
|
|
14
|
+
const isVigorDefault = (v) => v === VigorDefault;
|
|
15
|
+
const VigorEmpty = Symbol("VigorEmpty");
|
|
16
|
+
/** Type guard checking whether a value is the {@link VigorEmpty} sentinel. */
|
|
17
|
+
const isVigorEmpty = (v) => v === VigorEmpty;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Base class for Vigor's immutable, chainable builder objects.
|
|
21
|
+
*
|
|
22
|
+
* Each subclass holds a `base` config and a current `config`, and exposes
|
|
23
|
+
* `_next()` to derive a new instance with a patch merged in. Subclasses
|
|
24
|
+
* implement `_create()` to construct the concrete instance type, and the
|
|
25
|
+
* return type of `_next()` is computed through the `TL` type lambda so
|
|
26
|
+
* chained calls stay precisely typed.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam Base - Shape of the full config for this builder.
|
|
29
|
+
* @typeParam Config - Concrete config type held by this instance (extends `Base`).
|
|
30
|
+
* @typeParam TL - Type lambda mapping a config type to the concrete instance type.
|
|
31
|
+
*/
|
|
32
|
+
class VigorStatus {
|
|
33
|
+
_base;
|
|
34
|
+
_config;
|
|
35
|
+
/**
|
|
36
|
+
* @param base - The default/base config for this builder.
|
|
37
|
+
* @param config - The (possibly partial) config to merge onto `base`.
|
|
38
|
+
*/
|
|
39
|
+
constructor(base, config) {
|
|
40
|
+
this._base = base;
|
|
41
|
+
this._config = this._merge(base, config);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Derives a new instance by merging `patch` onto the current config.
|
|
45
|
+
*
|
|
46
|
+
* @param patch - Values to overwrite/merge in for this step.
|
|
47
|
+
* @param strategy - Optional tree mirroring the shape of `patch`, whose
|
|
48
|
+
* leaves may be `"replace" | "concat" | "merge"` to force the merge
|
|
49
|
+
* behavior at that path. Paths without a strategy fall back to the
|
|
50
|
+
* default behavior (deep merge for objects, concat for arrays).
|
|
51
|
+
*/
|
|
52
|
+
_next(patch, strategy) {
|
|
53
|
+
const merged = this._merge(this._config, patch, strategy);
|
|
54
|
+
return this._create(merged);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Deep-merges `target` onto `source`, honoring an optional per-path
|
|
58
|
+
* `strategy` tree. Objects merge key by key, arrays concatenate (unless
|
|
59
|
+
* `"replace"` is specified), and any other value is overwritten.
|
|
60
|
+
*/
|
|
61
|
+
_merge(source, target, strategy) {
|
|
62
|
+
const isObj = (v) => v !== null &&
|
|
63
|
+
typeof v === "object" &&
|
|
64
|
+
Object.getPrototypeOf(v) === Object.prototype;
|
|
65
|
+
const merge = (a, b, s) => {
|
|
66
|
+
if (b === undefined || b === null)
|
|
67
|
+
return a;
|
|
68
|
+
if (s === "replace")
|
|
69
|
+
return b;
|
|
70
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
71
|
+
return s === "replace" ? b : [...a, ...b];
|
|
72
|
+
}
|
|
73
|
+
if (isObj(a) && isObj(b)) {
|
|
74
|
+
const r = { ...a };
|
|
75
|
+
const sObj = isObj(s) ? s : undefined;
|
|
76
|
+
for (const k of Object.keys(b)) {
|
|
77
|
+
r[k] = merge(a[k], b[k], sObj?.[k]);
|
|
78
|
+
}
|
|
79
|
+
return r;
|
|
80
|
+
}
|
|
81
|
+
return b;
|
|
82
|
+
};
|
|
83
|
+
return merge(source, target, strategy);
|
|
84
|
+
}
|
|
85
|
+
/** The default/base config this builder was constructed with. */
|
|
86
|
+
get base() {
|
|
87
|
+
return this._base;
|
|
88
|
+
}
|
|
89
|
+
/** The current, merged config for this builder instance. */
|
|
90
|
+
get config() {
|
|
91
|
+
return this._config;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const VigorAllPolicySettingsBase = {
|
|
96
|
+
__brand: createBrand("All", "Policy<Settings<Schema"),
|
|
97
|
+
concurrency: 5,
|
|
98
|
+
onlySuccess: false
|
|
16
99
|
};
|
|
100
|
+
/** Immutable builder for an all-policy's general settings. */
|
|
101
|
+
class VigorAllPolicySettings extends VigorStatus {
|
|
102
|
+
constructor(config = VigorAllBase) {
|
|
103
|
+
super(VigorAllBase, config);
|
|
104
|
+
}
|
|
105
|
+
_create(config) {
|
|
106
|
+
return new VigorAllPolicySettings(config);
|
|
107
|
+
}
|
|
108
|
+
/** Sets the maximum number of tasks run concurrently. */
|
|
109
|
+
concurrency(num) {
|
|
110
|
+
return this._next({ policy: { settings: { concurrency: num } } });
|
|
111
|
+
}
|
|
112
|
+
/** When enabled, filters the final result down to only the tasks that succeeded. */
|
|
113
|
+
onlySuccess(bool) {
|
|
114
|
+
return this._next({ policy: { settings: { onlySuccess: bool } } });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const VigorAllPolicyMiddlewaresBase = {
|
|
119
|
+
__brand: createBrand("All", "Policy<Middlewares<Schema"),
|
|
120
|
+
before: [],
|
|
121
|
+
after: [],
|
|
122
|
+
onError: []
|
|
123
|
+
};
|
|
124
|
+
/** Immutable builder for an all-policy's per-task middleware pipeline (`before`, `after`, `onError`). */
|
|
125
|
+
class VigorAllPolicyMiddlewares extends VigorStatus {
|
|
126
|
+
constructor(config = VigorAllBase) {
|
|
127
|
+
super(VigorAllBase, config);
|
|
128
|
+
}
|
|
129
|
+
_create(config) {
|
|
130
|
+
return new VigorAllPolicyMiddlewares(config);
|
|
131
|
+
}
|
|
132
|
+
/** Registers a middleware that runs before each task is invoked. */
|
|
133
|
+
before(func) {
|
|
134
|
+
return this._next({ policy: { middlewares: { before: [func] } } });
|
|
135
|
+
}
|
|
136
|
+
/** Registers a middleware that runs after each task settles successfully. */
|
|
137
|
+
after(func) {
|
|
138
|
+
return this._next({ policy: { middlewares: { after: [func] } } });
|
|
139
|
+
}
|
|
140
|
+
/** Registers a middleware that runs when an individual task fails. */
|
|
141
|
+
onError(func) {
|
|
142
|
+
return this._next({ policy: { middlewares: { onError: [func] } } });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const VigorAllPolicyBase = {
|
|
147
|
+
__brand: createBrand("All", "Policy<Schema"),
|
|
148
|
+
target: [],
|
|
149
|
+
settings: VigorAllPolicySettingsBase,
|
|
150
|
+
middlewares: VigorAllPolicyMiddlewaresBase
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const VigorAllContextBase = {
|
|
154
|
+
__brand: createBrand("All", "Context<Schema"),
|
|
155
|
+
result: VigorDefault,
|
|
156
|
+
policy: VigorAllPolicyBase,
|
|
157
|
+
record: {}
|
|
158
|
+
};
|
|
159
|
+
const VigorAllEachContextBase = {
|
|
160
|
+
__brand: createBrand("All", "EachContext<Schema"),
|
|
161
|
+
result: VigorDefault,
|
|
162
|
+
error: VigorDefault,
|
|
163
|
+
flags: { overrided: false },
|
|
164
|
+
record: {}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Base class for all Vigor error types. Subclasses (`VigorRetryError`,
|
|
169
|
+
* `VigorFetchError`, `VigorParseError`, `VigorAllError`) fix `C`, `D`, and
|
|
170
|
+
* `T` to their own error-code union, data shape, and context shape.
|
|
171
|
+
*
|
|
172
|
+
* @typeParam C - Union of error codes for the subclass.
|
|
173
|
+
* @typeParam D - Shape of the structured error data.
|
|
174
|
+
* @typeParam T - Shape of the domain context.
|
|
175
|
+
*/
|
|
17
176
|
class VigorError extends Error {
|
|
18
177
|
timestamp = new Date();
|
|
19
178
|
cause;
|
|
20
179
|
code;
|
|
21
180
|
data;
|
|
22
181
|
method;
|
|
23
|
-
stats;
|
|
24
182
|
context;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
183
|
+
/**
|
|
184
|
+
* @param code - Machine-readable error code.
|
|
185
|
+
* @param message - Human-readable message (rendered after the `[code]` prefix).
|
|
186
|
+
* @param options - Additional error metadata (cause, data, method, context).
|
|
187
|
+
*/
|
|
188
|
+
constructor(code, message, options) {
|
|
189
|
+
super(`[${code}] ${message}`);
|
|
29
190
|
this.name = new.target.name;
|
|
30
191
|
this.code = code;
|
|
31
192
|
this.cause = options.cause;
|
|
32
193
|
this.data = options.data;
|
|
33
194
|
this.method = options.method;
|
|
34
|
-
this.stats = options.stats;
|
|
35
195
|
this.context = options.context;
|
|
36
196
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
37
197
|
Error.captureStackTrace?.(this, new.target);
|
|
38
198
|
}
|
|
39
199
|
}
|
|
40
|
-
|
|
200
|
+
|
|
201
|
+
const VigorAllErrorMessageFuncs = {
|
|
202
|
+
EMPTY_TARGET: (_) => `Empty target list`,
|
|
203
|
+
};
|
|
204
|
+
/** Error type thrown by the all module. */
|
|
205
|
+
class VigorAllError extends VigorError {
|
|
206
|
+
/**
|
|
207
|
+
* @param code - One of the all module's error codes.
|
|
208
|
+
* @param options - Error metadata, including the `data` payload for `code`.
|
|
209
|
+
*/
|
|
41
210
|
constructor(code, options) {
|
|
42
|
-
|
|
211
|
+
const messageFn = VigorAllErrorMessageFuncs[code];
|
|
212
|
+
super(code, messageFn(options.data), options);
|
|
43
213
|
}
|
|
44
214
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
215
|
+
|
|
216
|
+
const VigorAllBase = {
|
|
217
|
+
__brand: createBrand("All", "Schema"),
|
|
218
|
+
policy: VigorAllPolicyBase,
|
|
219
|
+
context: VigorAllContextBase
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Immutable builder that runs a list of independent tasks with bounded
|
|
223
|
+
* concurrency, per-task middleware, and either an all-results or
|
|
224
|
+
* only-successes result mode.
|
|
225
|
+
*/
|
|
226
|
+
class VigorAll extends VigorStatus {
|
|
227
|
+
constructor(config = VigorAllBase) {
|
|
228
|
+
super(VigorAllBase, config);
|
|
48
229
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
constructor(code, options) {
|
|
52
|
-
super(code, options);
|
|
230
|
+
_create(config) {
|
|
231
|
+
return new VigorAll(config);
|
|
53
232
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
super(code, options);
|
|
233
|
+
/** Sets the list of tasks to run. */
|
|
234
|
+
target(...funcs) {
|
|
235
|
+
return this._next({ policy: { target: funcs } });
|
|
58
236
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.ctor = ctor;
|
|
67
|
-
this._config = { ...this._base, ...(config || {}) };
|
|
68
|
-
}
|
|
69
|
-
_mergeConfig(source, target) {
|
|
70
|
-
const isPlainObject = (val) => val !== null && typeof val === 'object' && Object.getPrototypeOf(val) === Object.prototype;
|
|
71
|
-
if (target === undefined || target === null) {
|
|
72
|
-
return source;
|
|
237
|
+
/** Configures the all-policy's general settings (concurrency, onlySuccess). */
|
|
238
|
+
settings(input) {
|
|
239
|
+
if (typeof input === 'function' || input instanceof VigorAllPolicySettings) {
|
|
240
|
+
const result = typeof input === 'function'
|
|
241
|
+
? input(new VigorAllPolicySettings())
|
|
242
|
+
: input;
|
|
243
|
+
return this._next(result.config);
|
|
73
244
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
245
|
+
return this._next({ policy: { settings: input } });
|
|
246
|
+
}
|
|
247
|
+
/** Configures the all-policy's per-task middleware pipeline. */
|
|
248
|
+
middlewares(input) {
|
|
249
|
+
if (typeof input === 'function' || input instanceof VigorAllPolicyMiddlewares) {
|
|
250
|
+
const result = typeof input === 'function'
|
|
251
|
+
? input(new VigorAllPolicyMiddlewares())
|
|
252
|
+
: input;
|
|
253
|
+
return this._next(result.config);
|
|
80
254
|
}
|
|
81
|
-
|
|
82
|
-
|
|
255
|
+
return this._next({ policy: { middlewares: input } });
|
|
256
|
+
}
|
|
257
|
+
/** Runs a single task through the before/after/onError middleware pipeline. */
|
|
258
|
+
async _runTask(task, policy) {
|
|
259
|
+
let ctx = { ...VigorAllEachContextBase };
|
|
260
|
+
try {
|
|
261
|
+
for (const before of policy.middlewares.before)
|
|
262
|
+
await before(ctx);
|
|
263
|
+
ctx.result = await task();
|
|
264
|
+
for (const after of policy.middlewares.after)
|
|
265
|
+
await after(ctx);
|
|
266
|
+
return { success: true, value: ctx.result };
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
ctx.error = error;
|
|
270
|
+
const setResult = (r) => { ctx.flags.overrided = true; ctx.result = r; };
|
|
271
|
+
for (const onError of policy.middlewares.onError)
|
|
272
|
+
await onError(ctx, { setResult });
|
|
273
|
+
if (ctx.flags.overrided)
|
|
274
|
+
return { success: true, value: ctx.result };
|
|
275
|
+
return { success: false, value: error };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/** Runs all tasks and returns the results array directly, throwing on final failure. */
|
|
279
|
+
async request() {
|
|
280
|
+
const res = await this.requestVerbose();
|
|
281
|
+
if (res.success)
|
|
282
|
+
return res.data;
|
|
283
|
+
throw res.error;
|
|
284
|
+
}
|
|
285
|
+
/** Runs all tasks and returns a result/error envelope instead of throwing. */
|
|
286
|
+
async requestVerbose() {
|
|
287
|
+
return await this.requestRuntime();
|
|
288
|
+
}
|
|
289
|
+
/** Internal execution loop implementing the bounded-concurrency worker pool. */
|
|
290
|
+
async requestRuntime(restartAttempt = 1) {
|
|
291
|
+
const config = this._merge(this.config, {});
|
|
292
|
+
const policy = config.policy;
|
|
293
|
+
try {
|
|
294
|
+
if (policy.target.length === 0)
|
|
295
|
+
throw new VigorAllError("EMPTY_TARGET", {
|
|
296
|
+
method: "request",
|
|
297
|
+
data: {}
|
|
298
|
+
});
|
|
299
|
+
const concurrency = Math.max(1, policy.settings.concurrency);
|
|
300
|
+
const results = new Array(policy.target.length);
|
|
301
|
+
let cursor = 0;
|
|
302
|
+
const worker = async () => {
|
|
303
|
+
while (true) {
|
|
304
|
+
const i = cursor++;
|
|
305
|
+
if (i >= policy.target.length)
|
|
306
|
+
return;
|
|
307
|
+
results[i] = await this._runTask(policy.target[i], policy);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, policy.target.length) }, () => worker()));
|
|
311
|
+
const result = policy.settings.onlySuccess
|
|
312
|
+
? results.filter(r => r.success).map(r => r.value)
|
|
313
|
+
: results.map(r => r.value);
|
|
314
|
+
return {
|
|
315
|
+
success: true,
|
|
316
|
+
data: result,
|
|
317
|
+
error: null
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
return {
|
|
322
|
+
success: false,
|
|
323
|
+
data: null,
|
|
324
|
+
error
|
|
325
|
+
};
|
|
83
326
|
}
|
|
84
|
-
return target;
|
|
85
327
|
}
|
|
86
|
-
_next(config) { return this.ctor(this._mergeConfig(this._config, config)); }
|
|
87
|
-
_getConfig() { return this._config; }
|
|
88
|
-
_getBase() { return this._base; }
|
|
89
328
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
329
|
+
|
|
330
|
+
const VigorFetchPolicySettingsBase = {
|
|
331
|
+
__brand: createBrand("Fetch", "Policy<Settings<Schema"),
|
|
332
|
+
retryHeaders: ["retry-after", "ratelimit-reset", "x-ratelimit-reset", "x-retry-after", "x-amz-retry-after", "chrome-proxy-next-link"],
|
|
333
|
+
unretryStatus: [400, 401, 403, 404, 405, 413, 422],
|
|
334
|
+
maxRestarts: 3,
|
|
335
|
+
default: VigorEmpty
|
|
336
|
+
};
|
|
337
|
+
/** Immutable builder for a fetch policy's general settings. */
|
|
338
|
+
class VigorFetchPolicySettings extends VigorStatus {
|
|
339
|
+
constructor(config = VigorFetchBase) {
|
|
340
|
+
super(VigorFetchBase, config);
|
|
341
|
+
}
|
|
342
|
+
_create(config) {
|
|
343
|
+
return new VigorFetchPolicySettings(config);
|
|
344
|
+
}
|
|
345
|
+
/** Sets response headers consulted to compute a server-suggested retry delay. */
|
|
346
|
+
retryHeaders(...strs) {
|
|
347
|
+
return this._next({ policy: { settings: { retryHeaders: strs } } });
|
|
348
|
+
}
|
|
349
|
+
/** Sets HTTP status codes that should never be retried. */
|
|
350
|
+
unretryStatus(...nums) {
|
|
351
|
+
return this._next({ policy: { settings: { unretryStatus: nums } } });
|
|
352
|
+
}
|
|
353
|
+
/** Sets the maximum number of full restarts allowed. */
|
|
354
|
+
maxRestarts(num) {
|
|
355
|
+
return this._next({ policy: { settings: { maxRestarts: num } } });
|
|
356
|
+
}
|
|
357
|
+
/** Sets the fallback value/factory used when the request ultimately fails. */
|
|
358
|
+
default(func) {
|
|
359
|
+
return this._next({ policy: { settings: { default: func } } });
|
|
100
360
|
}
|
|
101
|
-
default(unk) { return this._next({ default: unk }); }
|
|
102
|
-
timeout(num) { return this._next({ timeout: num }); }
|
|
103
|
-
attempt(num) { return this._next({ attempt: num }); }
|
|
104
|
-
jitter(num) { return this._next({ jitter: num }); }
|
|
105
361
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
362
|
+
|
|
363
|
+
const VigorFetchPolicyMiddlewaresBase = {
|
|
364
|
+
__brand: createBrand("Fetch", "Policy<Middlewares<Schema"),
|
|
365
|
+
before: [],
|
|
366
|
+
after: [],
|
|
367
|
+
onResult: [],
|
|
368
|
+
onError: []
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Immutable builder for a fetch policy's middleware pipeline (`before`,
|
|
372
|
+
* `after`, `onResult`, `onError`). Each stage accepts either "fluent"
|
|
373
|
+
* middleware (returns a plain value merged into the context) or
|
|
374
|
+
* "intercept" middleware (receives an explicit API to mutate control flow).
|
|
375
|
+
*/
|
|
376
|
+
class VigorFetchPolicyMiddlewares extends VigorStatus {
|
|
377
|
+
constructor(config = VigorFetchBase) {
|
|
378
|
+
super(VigorFetchBase, config);
|
|
379
|
+
}
|
|
380
|
+
_create(config) {
|
|
381
|
+
return new VigorFetchPolicyMiddlewares(config);
|
|
382
|
+
}
|
|
383
|
+
before(type, func) {
|
|
384
|
+
return this._next({ policy: { middlewares: { before: [{ _mode: type, func }] } } });
|
|
385
|
+
}
|
|
386
|
+
after(type, func) {
|
|
387
|
+
return this._next({ policy: { middlewares: { after: [{ _mode: type, func }] } } });
|
|
388
|
+
}
|
|
389
|
+
onResult(type, func) {
|
|
390
|
+
return this._next({ policy: { middlewares: { onResult: [{ _mode: type, func }] } } });
|
|
391
|
+
}
|
|
392
|
+
onError(type, func) {
|
|
393
|
+
return this._next({ policy: { middlewares: { onError: [{ _mode: type, func }] } } });
|
|
394
|
+
}
|
|
124
395
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
396
|
+
|
|
397
|
+
const VigorRetryPolicySettingsBase = {
|
|
398
|
+
__brand: createBrand("Retry", "Policy<Settings<Schema"),
|
|
399
|
+
default: VigorEmpty,
|
|
400
|
+
maxAttempts: 5,
|
|
401
|
+
maxRestarts: 3,
|
|
402
|
+
timeout: 20000
|
|
403
|
+
};
|
|
404
|
+
/** Immutable builder for a retry policy's general settings. */
|
|
405
|
+
class VigorRetryPolicySettings extends VigorStatus {
|
|
406
|
+
constructor(config = VigorRetryBase) {
|
|
407
|
+
super(VigorRetryBase, config);
|
|
131
408
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
409
|
+
_create(config) {
|
|
410
|
+
return new VigorRetryPolicySettings(config);
|
|
411
|
+
}
|
|
412
|
+
/** Sets the fallback value/factory used when the target ultimately fails. */
|
|
413
|
+
default(func) {
|
|
414
|
+
return this._next({
|
|
415
|
+
policy: { settings: { default: func } }
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/** Sets the maximum number of retry attempts before giving up. */
|
|
419
|
+
maxAttempts(num) {
|
|
420
|
+
return this._next({
|
|
421
|
+
policy: { settings: { maxAttempts: num } }
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
/** Sets the maximum number of full restarts allowed. */
|
|
425
|
+
maxRestarts(num) {
|
|
426
|
+
return this._next({
|
|
427
|
+
policy: { settings: { maxRestarts: num } }
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/** Sets the timeout, in milliseconds, for a single attempt. */
|
|
431
|
+
timeout(num) {
|
|
432
|
+
return this._next({
|
|
433
|
+
policy: { settings: { timeout: num } }
|
|
434
|
+
});
|
|
135
435
|
}
|
|
136
436
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
437
|
+
|
|
438
|
+
const VigorRetryPolicyMiddlewaresBase = {
|
|
439
|
+
__brand: createBrand("Retry", "Policy<Middlewares<Schema"),
|
|
440
|
+
before: [],
|
|
441
|
+
after: [],
|
|
442
|
+
onResult: [],
|
|
443
|
+
retryIf: [],
|
|
444
|
+
onRetry: [],
|
|
445
|
+
onError: []
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* Immutable builder for a retry policy's middleware pipeline (`before`,
|
|
449
|
+
* `after`, `onResult`, `retryIf`, `onRetry`, `onError`). Each stage accepts
|
|
450
|
+
* either "fluent" middleware (returns a plain value merged into the
|
|
451
|
+
* context) or "intercept" middleware (receives an explicit API to mutate
|
|
452
|
+
* control flow).
|
|
453
|
+
*/
|
|
454
|
+
class VigorRetryPolicyMiddlewares extends VigorStatus {
|
|
455
|
+
constructor(config = VigorRetryBase) {
|
|
456
|
+
super(VigorRetryBase, config);
|
|
457
|
+
}
|
|
458
|
+
_create(config) {
|
|
459
|
+
return new VigorRetryPolicyMiddlewares(config);
|
|
460
|
+
}
|
|
461
|
+
before(type, func) {
|
|
462
|
+
return this._next({
|
|
463
|
+
policy: { middlewares: { before: [{ _mode: type, func }] } }
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
after(type, func) {
|
|
467
|
+
return this._next({
|
|
468
|
+
policy: { middlewares: { after: [{ _mode: type, func }] } }
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
onResult(type, func) {
|
|
472
|
+
return this._next({
|
|
473
|
+
policy: { middlewares: { onResult: [{ _mode: type, func }] } }
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
retryIf(type, func) {
|
|
477
|
+
return this._next({
|
|
478
|
+
policy: { middlewares: { retryIf: [{ _mode: type, func }] } }
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
onRetry(type, func) {
|
|
482
|
+
return this._next({
|
|
483
|
+
policy: { middlewares: { onRetry: [{ _mode: type, func }] } }
|
|
484
|
+
});
|
|
146
485
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
_calculateDelay(attempt) {
|
|
152
|
-
const { initial, increment, minDelay, maxDelay } = this._config;
|
|
153
|
-
return Math.max(minDelay, Math.min(maxDelay, initial + increment * attempt));
|
|
486
|
+
onError(type, func) {
|
|
487
|
+
return this._next({
|
|
488
|
+
policy: { middlewares: { onError: [{ _mode: type, func }] } }
|
|
489
|
+
});
|
|
154
490
|
}
|
|
155
491
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
492
|
+
|
|
493
|
+
const VigorRetryErrorMessageFuncs = {
|
|
494
|
+
VALUE_REQUIRED: ({ key }) => `Value Required (missing ${key})`,
|
|
495
|
+
RETRY_EXHAUSTED: ({ maxAttempts }) => `Retry exhausted, (max ${maxAttempts})`,
|
|
496
|
+
RESTART_EXHAUSTED: ({ maxAttempts }) => `Restart exhausted, (max ${maxAttempts})`,
|
|
497
|
+
TIMED_OUT: ({ limit }) => `Timeout: exceeded ${limit}ms`,
|
|
498
|
+
};
|
|
499
|
+
/** Error type thrown by the retry module. */
|
|
500
|
+
class VigorRetryError extends VigorError {
|
|
501
|
+
/**
|
|
502
|
+
* @param code - One of the retry module's error codes.
|
|
503
|
+
* @param options - Error metadata, including the `data` payload for `code`.
|
|
504
|
+
*/
|
|
505
|
+
constructor(code, options) {
|
|
506
|
+
const messageFn = VigorRetryErrorMessageFuncs[code];
|
|
507
|
+
super(code, messageFn(options.data), options);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** Returns a random offset in the range `[-jitter, +jitter]` to randomize a computed delay. */
|
|
512
|
+
function VigorJitter(jitter) {
|
|
513
|
+
return (Math.random() * 2 - 1) * jitter;
|
|
514
|
+
}
|
|
515
|
+
const VigorRetryPolicyAlgorithmsConstantBase = {
|
|
516
|
+
__brand: createBrand("Retry", "Policy<Algorithms<Constant<Schema"),
|
|
517
|
+
_tag: "constant",
|
|
518
|
+
jitter: 1000,
|
|
519
|
+
interval: 2000,
|
|
520
|
+
_calculateDelay: (att, config) => {
|
|
521
|
+
const { interval, jitter } = config;
|
|
522
|
+
const min = jitter;
|
|
523
|
+
let delay = Math.max(min, interval);
|
|
524
|
+
delay += VigorJitter(jitter);
|
|
525
|
+
return delay;
|
|
166
526
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return
|
|
527
|
+
};
|
|
528
|
+
/** Immutable builder for the constant-interval retry delay algorithm. */
|
|
529
|
+
class VigorRetryPolicyAlgorithmsConstant extends VigorStatus {
|
|
530
|
+
constructor(config = VigorRetryPolicyAlgorithmsConstantBase) {
|
|
531
|
+
super(VigorRetryPolicyAlgorithmsConstantBase, config);
|
|
532
|
+
}
|
|
533
|
+
_create(config) {
|
|
534
|
+
return new VigorRetryPolicyAlgorithmsConstant(config);
|
|
535
|
+
}
|
|
536
|
+
/** Sets the amount of random jitter added to the computed delay. */
|
|
537
|
+
jitter(num) {
|
|
538
|
+
return this._next({ jitter: num });
|
|
539
|
+
}
|
|
540
|
+
/** Sets the fixed delay interval, in milliseconds. */
|
|
541
|
+
interval(num) {
|
|
542
|
+
return this._next({ interval: num });
|
|
175
543
|
}
|
|
176
544
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
545
|
+
const VigorRetryPolicyAlgorithmsLinearBase = {
|
|
546
|
+
__brand: createBrand("Retry", "Policy<Algorithms<Linear<Schema"),
|
|
547
|
+
_tag: "linear",
|
|
548
|
+
jitter: 1000,
|
|
549
|
+
initial: 500,
|
|
550
|
+
increment: 1000,
|
|
551
|
+
minDelay: 0,
|
|
552
|
+
maxDelay: 10000,
|
|
553
|
+
_calculateDelay: (att, config) => {
|
|
554
|
+
const { initial, jitter, increment, minDelay, maxDelay } = config;
|
|
555
|
+
const range = maxDelay - minDelay;
|
|
556
|
+
const j = Math.min(jitter, range / 2);
|
|
557
|
+
const min = minDelay + j;
|
|
558
|
+
const max = maxDelay - j;
|
|
559
|
+
let delay = initial + increment * att;
|
|
560
|
+
delay = Math.max(min, Math.min(max, delay));
|
|
561
|
+
delay += VigorJitter(j);
|
|
562
|
+
return delay;
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
/** Immutable builder for the linearly-increasing retry delay algorithm. */
|
|
566
|
+
class VigorRetryPolicyAlgorithmsLinear extends VigorStatus {
|
|
567
|
+
constructor(config = VigorRetryPolicyAlgorithmsLinearBase) {
|
|
568
|
+
super(VigorRetryPolicyAlgorithmsLinearBase, config);
|
|
569
|
+
}
|
|
570
|
+
_create(config) {
|
|
571
|
+
return new VigorRetryPolicyAlgorithmsLinear(config);
|
|
572
|
+
}
|
|
573
|
+
/** Sets the amount of random jitter added to the computed delay. */
|
|
574
|
+
jitter(num) {
|
|
575
|
+
return this._next({ jitter: num });
|
|
576
|
+
}
|
|
577
|
+
/** Sets the initial delay, in milliseconds, before the first retry. */
|
|
578
|
+
initial(num) {
|
|
579
|
+
return this._next({ initial: num });
|
|
185
580
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
581
|
+
/** Sets how much the delay grows, in milliseconds, per attempt. */
|
|
582
|
+
increment(num) {
|
|
583
|
+
return this._next({ increment: num });
|
|
584
|
+
}
|
|
585
|
+
/** Sets the minimum allowed delay, in milliseconds. */
|
|
586
|
+
minDelay(num) {
|
|
587
|
+
return this._next({ minDelay: num });
|
|
588
|
+
}
|
|
589
|
+
/** Sets the maximum allowed delay, in milliseconds. */
|
|
590
|
+
maxDelay(num) {
|
|
591
|
+
return this._next({ maxDelay: num });
|
|
190
592
|
}
|
|
191
593
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return
|
|
211
|
-
timeline.push({
|
|
212
|
-
action: action,
|
|
213
|
-
content: content,
|
|
214
|
-
time: Date.now()
|
|
215
|
-
});
|
|
216
|
-
};
|
|
594
|
+
const VigorRetryPolicyAlgorithmsBackoffBase = {
|
|
595
|
+
__brand: createBrand("Retry", "Policy<Algorithms<Backoff<Schema"),
|
|
596
|
+
_tag: "backoff",
|
|
597
|
+
jitter: 800,
|
|
598
|
+
initial: 0,
|
|
599
|
+
multiplier: 1.7,
|
|
600
|
+
unit: 1000,
|
|
601
|
+
minDelay: 500,
|
|
602
|
+
maxDelay: 10000,
|
|
603
|
+
_calculateDelay: (att, config) => {
|
|
604
|
+
const { jitter, initial, multiplier, unit, minDelay, maxDelay } = config;
|
|
605
|
+
const range = maxDelay - minDelay;
|
|
606
|
+
const j = Math.min(jitter, range / 2);
|
|
607
|
+
const min = minDelay + j;
|
|
608
|
+
const max = maxDelay - j;
|
|
609
|
+
let delay = initial + unit * Math.pow(multiplier, att);
|
|
610
|
+
delay = Math.max(min, Math.min(max, delay));
|
|
611
|
+
delay += VigorJitter(j);
|
|
612
|
+
return delay;
|
|
217
613
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
614
|
+
};
|
|
615
|
+
/** Immutable builder for the exponential-backoff retry delay algorithm. */
|
|
616
|
+
class VigorRetryPolicyAlgorithmsBackoff extends VigorStatus {
|
|
617
|
+
constructor(config = VigorRetryPolicyAlgorithmsBackoffBase) {
|
|
618
|
+
super(VigorRetryPolicyAlgorithmsBackoffBase, config);
|
|
619
|
+
}
|
|
620
|
+
_create(config) {
|
|
621
|
+
return new VigorRetryPolicyAlgorithmsBackoff(config);
|
|
622
|
+
}
|
|
623
|
+
/** Sets the amount of random jitter added to the computed delay. */
|
|
624
|
+
jitter(num) {
|
|
625
|
+
return this._next({ jitter: num });
|
|
626
|
+
}
|
|
627
|
+
/** Sets the initial delay, in milliseconds, before backoff growth is applied. */
|
|
628
|
+
initial(num) {
|
|
629
|
+
return this._next({ initial: num });
|
|
630
|
+
}
|
|
631
|
+
/** Sets the exponential growth factor applied per attempt. */
|
|
632
|
+
multiplier(num) {
|
|
633
|
+
return this._next({ multiplier: num });
|
|
634
|
+
}
|
|
635
|
+
/** Sets the base unit, in milliseconds, the multiplier is scaled by. */
|
|
636
|
+
unit(num) {
|
|
637
|
+
return this._next({ unit: num });
|
|
638
|
+
}
|
|
639
|
+
/** Sets the minimum allowed delay, in milliseconds. */
|
|
640
|
+
minDelay(num) {
|
|
641
|
+
return this._next({ minDelay: num });
|
|
642
|
+
}
|
|
643
|
+
/** Sets the maximum allowed delay, in milliseconds. */
|
|
644
|
+
maxDelay(num) {
|
|
645
|
+
return this._next({ maxDelay: num });
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const VigorRetryPolicyAlgorithmsCustomBase = {
|
|
649
|
+
__brand: createBrand("Retry", "Policy<Algorithms<Custom<Schema"),
|
|
650
|
+
_tag: "custom",
|
|
651
|
+
jitter: 800,
|
|
652
|
+
minDelay: 500,
|
|
653
|
+
maxDelay: 10000,
|
|
654
|
+
target: VigorEmpty,
|
|
655
|
+
_calculateDelay: (att, config) => {
|
|
656
|
+
const { jitter, minDelay, maxDelay, target } = config;
|
|
657
|
+
if (isVigorEmpty(target))
|
|
658
|
+
throw new VigorRetryError("VALUE_REQUIRED", {
|
|
659
|
+
method: "_calculateDelay",
|
|
660
|
+
data: {
|
|
661
|
+
key: "target"
|
|
662
|
+
}
|
|
236
663
|
});
|
|
237
|
-
|
|
664
|
+
const range = maxDelay - minDelay;
|
|
665
|
+
const j = Math.min(jitter, range / 2);
|
|
666
|
+
const min = minDelay + j;
|
|
667
|
+
const max = maxDelay - j;
|
|
668
|
+
let delay = target(att);
|
|
669
|
+
delay = Math.max(min, Math.min(max, delay));
|
|
670
|
+
delay += VigorJitter(j);
|
|
671
|
+
return delay;
|
|
238
672
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
673
|
+
};
|
|
674
|
+
/** Immutable builder for a user-supplied custom retry delay algorithm. */
|
|
675
|
+
class VigorRetryPolicyAlgorithmsCustom extends VigorStatus {
|
|
676
|
+
constructor(config = VigorRetryPolicyAlgorithmsCustomBase) {
|
|
677
|
+
super(VigorRetryPolicyAlgorithmsCustomBase, config);
|
|
678
|
+
}
|
|
679
|
+
_create(config) {
|
|
680
|
+
return new VigorRetryPolicyAlgorithmsCustom(config);
|
|
681
|
+
}
|
|
682
|
+
/** Sets the amount of random jitter added to the computed delay. */
|
|
683
|
+
jitter(num) {
|
|
684
|
+
return this._next({ jitter: num });
|
|
685
|
+
}
|
|
686
|
+
/** Sets the minimum allowed delay, in milliseconds. */
|
|
687
|
+
minDelay(num) {
|
|
688
|
+
return this._next({ minDelay: num });
|
|
689
|
+
}
|
|
690
|
+
/** Sets the maximum allowed delay, in milliseconds. */
|
|
691
|
+
maxDelay(num) {
|
|
692
|
+
return this._next({ maxDelay: num });
|
|
693
|
+
}
|
|
694
|
+
/** Sets the user-supplied function computing the raw delay for an attempt. */
|
|
695
|
+
target(func) {
|
|
696
|
+
return this._next({ target: func });
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
const VigorRetryPolicyAlgorithmsBase = VigorRetryPolicyAlgorithmsConstantBase;
|
|
700
|
+
/**
|
|
701
|
+
* Immutable builder for selecting a retry policy's delay algorithm.
|
|
702
|
+
*
|
|
703
|
+
* Switching algorithms (`constant`/`linear`/`backoff`/`custom`) always
|
|
704
|
+
* replaces the previous algorithm's config wholesale via the `"replace"`
|
|
705
|
+
* merge strategy, since the different algorithms' fields are not
|
|
706
|
+
* compatible with one another.
|
|
707
|
+
*/
|
|
708
|
+
class VigorRetryPolicyAlgorithms extends VigorStatus {
|
|
709
|
+
constructor(config = VigorRetryBase) {
|
|
710
|
+
super(VigorRetryBase, config);
|
|
711
|
+
}
|
|
712
|
+
_create(config) {
|
|
713
|
+
return new VigorRetryPolicyAlgorithms(config);
|
|
714
|
+
}
|
|
715
|
+
/** Switches to the constant-interval delay algorithm. */
|
|
716
|
+
constant(input) {
|
|
717
|
+
const leaf = this._merge(VigorRetryPolicyAlgorithmsConstantBase, input ?? {});
|
|
718
|
+
return this._next({
|
|
719
|
+
policy: { algorithms: leaf }
|
|
720
|
+
}, { policy: { algorithms: "replace" } });
|
|
721
|
+
}
|
|
722
|
+
/** Switches to the linearly-increasing delay algorithm. */
|
|
723
|
+
linear(input) {
|
|
724
|
+
const leaf = this._merge(VigorRetryPolicyAlgorithmsLinearBase, input ?? {});
|
|
725
|
+
return this._next({
|
|
726
|
+
policy: { algorithms: leaf }
|
|
727
|
+
}, { policy: { algorithms: "replace" } });
|
|
728
|
+
}
|
|
729
|
+
/** Switches to the exponential-backoff delay algorithm. */
|
|
730
|
+
backoff(input) {
|
|
731
|
+
const leaf = this._merge(VigorRetryPolicyAlgorithmsBackoffBase, input ?? {});
|
|
732
|
+
return this._next({
|
|
733
|
+
policy: { algorithms: leaf }
|
|
734
|
+
}, { policy: { algorithms: "replace" } });
|
|
735
|
+
}
|
|
736
|
+
/** Switches to a user-supplied custom delay algorithm. */
|
|
737
|
+
custom(input) {
|
|
738
|
+
const leaf = this._merge(VigorRetryPolicyAlgorithmsCustomBase, input ?? {});
|
|
739
|
+
return this._next({
|
|
740
|
+
policy: { algorithms: leaf }
|
|
741
|
+
}, { policy: { algorithms: "replace" } });
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const VigorRetryPolicyBase = {
|
|
746
|
+
__brand: createBrand("Retry", "Policy<Schema"),
|
|
747
|
+
target: VigorEmpty,
|
|
748
|
+
abortSignals: [],
|
|
749
|
+
settings: VigorRetryPolicySettingsBase,
|
|
750
|
+
middlewares: VigorRetryPolicyMiddlewaresBase,
|
|
751
|
+
algorithms: VigorRetryPolicyAlgorithmsBase
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
const VigorRetryContextBase = {
|
|
755
|
+
__brand: createBrand("Retry", "Context<Schema"),
|
|
756
|
+
result: VigorDefault,
|
|
757
|
+
error: VigorDefault,
|
|
758
|
+
system: {
|
|
759
|
+
delay: VigorDefault,
|
|
760
|
+
attempt: 0
|
|
761
|
+
},
|
|
762
|
+
flags: {
|
|
763
|
+
doRetry: true,
|
|
764
|
+
doRestart: false,
|
|
765
|
+
brokeRetry: false,
|
|
766
|
+
overridden: false
|
|
767
|
+
},
|
|
768
|
+
record: {},
|
|
769
|
+
policy: VigorRetryPolicyBase
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
const VigorRetryBase = {
|
|
773
|
+
__brand: createBrand("Retry", "Schema"),
|
|
774
|
+
policy: VigorRetryPolicyBase,
|
|
775
|
+
context: VigorRetryContextBase
|
|
776
|
+
};
|
|
777
|
+
/**
|
|
778
|
+
* Immutable builder that executes a target function with retry, restart,
|
|
779
|
+
* timeout, and abort support, driven by a configurable delay algorithm and
|
|
780
|
+
* middleware pipeline.
|
|
781
|
+
*/
|
|
782
|
+
class VigorRetry extends VigorStatus {
|
|
783
|
+
constructor(config = VigorRetryBase) {
|
|
784
|
+
super(VigorRetryBase, config);
|
|
785
|
+
}
|
|
786
|
+
_create(config) {
|
|
787
|
+
return new VigorRetry(config);
|
|
788
|
+
}
|
|
789
|
+
/** Sets the target function to invoke, with retry/timeout applied. */
|
|
790
|
+
target(func) {
|
|
791
|
+
return this._next({
|
|
792
|
+
policy: { target: func }
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
/** Sets external abort signals that, when aborted, stop retrying. */
|
|
796
|
+
abortSignals(...sig) {
|
|
797
|
+
return this._next({
|
|
798
|
+
policy: { abortSignals: sig }
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
/** Configures the retry policy's general settings (max attempts, timeout, etc). */
|
|
802
|
+
settings(input) {
|
|
803
|
+
if (typeof input === 'function' || input instanceof VigorRetryPolicySettings) {
|
|
804
|
+
const result = typeof input === 'function'
|
|
805
|
+
? input(new VigorRetryPolicySettings())
|
|
806
|
+
: input;
|
|
807
|
+
return this._next(result.config);
|
|
246
808
|
}
|
|
247
|
-
return this._next({
|
|
809
|
+
return this._next({
|
|
810
|
+
policy: { settings: input }
|
|
811
|
+
});
|
|
248
812
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
813
|
+
/** Configures the retry policy's middleware pipeline. */
|
|
814
|
+
middlewares(input) {
|
|
815
|
+
if (typeof input === 'function' || input instanceof VigorRetryPolicyMiddlewares) {
|
|
816
|
+
const result = typeof input === 'function'
|
|
817
|
+
? input(new VigorRetryPolicyMiddlewares())
|
|
818
|
+
: input;
|
|
819
|
+
return this._next(result.config);
|
|
252
820
|
}
|
|
253
|
-
|
|
254
|
-
|
|
821
|
+
return this._next({
|
|
822
|
+
policy: { middlewares: input }
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
/** Configures the retry policy's delay algorithm. */
|
|
826
|
+
algorithms(input) {
|
|
827
|
+
if (typeof input === 'function') {
|
|
828
|
+
const result = input(new VigorRetryPolicyAlgorithms());
|
|
829
|
+
const algoSlice = result.config.policy.algorithms;
|
|
830
|
+
return this._next({
|
|
831
|
+
policy: { algorithms: algoSlice }
|
|
832
|
+
}, { policy: { algorithms: "replace" } });
|
|
255
833
|
}
|
|
256
|
-
return this._next({
|
|
834
|
+
return this._next({
|
|
835
|
+
policy: { algorithms: input }
|
|
836
|
+
}, { policy: { algorithms: "replace" } });
|
|
257
837
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
838
|
+
/** Runs the target and returns its result directly, throwing on final failure. */
|
|
839
|
+
async request() {
|
|
840
|
+
const res = await this.requestVerbose();
|
|
841
|
+
if (res.success)
|
|
842
|
+
return res.data;
|
|
843
|
+
throw res.error;
|
|
261
844
|
}
|
|
262
|
-
|
|
263
|
-
|
|
845
|
+
/** Runs the target and returns a result/error envelope instead of throwing. */
|
|
846
|
+
async requestVerbose() {
|
|
847
|
+
return await this.requestRuntime();
|
|
264
848
|
}
|
|
265
|
-
|
|
266
|
-
|
|
849
|
+
/** Internal execution loop implementing the attempt/retry/restart lifecycle. */
|
|
850
|
+
async requestRuntime(restartAttempt = 1) {
|
|
851
|
+
const config = this._merge(this.config, {});
|
|
267
852
|
let ctx = {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
853
|
+
...config.context,
|
|
854
|
+
policy: config.policy
|
|
855
|
+
};
|
|
856
|
+
if (isVigorEmpty(ctx.policy.target))
|
|
857
|
+
throw new VigorRetryError("VALUE_REQUIRED", {
|
|
858
|
+
method: "request",
|
|
859
|
+
data: {
|
|
860
|
+
key: "target"
|
|
861
|
+
},
|
|
862
|
+
context: ctx
|
|
863
|
+
});
|
|
864
|
+
const target = ctx.policy.target;
|
|
865
|
+
const throwError = (err) => {
|
|
866
|
+
throw err;
|
|
280
867
|
};
|
|
281
|
-
const addTimeline = this._createTimelineHandler(ctx.timeline);
|
|
282
|
-
const handleInterceptor = this._createInterceptorHandler(ctx, addTimeline);
|
|
283
|
-
addTimeline("PROCESS_HANDLING", {
|
|
284
|
-
type: "REQUEST_START",
|
|
285
|
-
data: {}
|
|
286
|
-
});
|
|
287
868
|
try {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
method: "request",
|
|
291
|
-
data: {
|
|
292
|
-
expected: ["function"],
|
|
293
|
-
received: stats.target
|
|
294
|
-
},
|
|
295
|
-
stats: stats,
|
|
296
|
-
context: ctx
|
|
297
|
-
});
|
|
298
|
-
while (ctx.attempt < stats.settings.attempt) {
|
|
299
|
-
ctx.attempt++;
|
|
300
|
-
addTimeline("ATTEMPT_INCREASED", {
|
|
301
|
-
attempt: ctx.attempt
|
|
302
|
-
});
|
|
869
|
+
while (ctx.system.attempt < ctx.policy.settings.maxAttempts) {
|
|
870
|
+
ctx.system.attempt++;
|
|
303
871
|
try {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
const
|
|
872
|
+
const breakRetry = (err) => {
|
|
873
|
+
ctx.flags.brokeRetry = true;
|
|
874
|
+
throw err;
|
|
875
|
+
};
|
|
876
|
+
const userController = new AbortController();
|
|
309
877
|
const timeoutController = new AbortController();
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
args: [error]
|
|
338
|
-
});
|
|
339
|
-
throw error;
|
|
878
|
+
const innerSignals = AbortSignal.any([
|
|
879
|
+
userController.signal,
|
|
880
|
+
timeoutController.signal
|
|
881
|
+
]);
|
|
882
|
+
const outerSignals = AbortSignal.any(ctx.policy.abortSignals);
|
|
883
|
+
const autoBreak = () => {
|
|
884
|
+
ctx.flags.brokeRetry = true;
|
|
885
|
+
};
|
|
886
|
+
outerSignals.addEventListener("abort", autoBreak);
|
|
887
|
+
const mergedSignals = AbortSignal.any([
|
|
888
|
+
innerSignals,
|
|
889
|
+
outerSignals
|
|
890
|
+
]);
|
|
891
|
+
let onAbort;
|
|
892
|
+
let timeoutId;
|
|
893
|
+
try {
|
|
894
|
+
for (const middleware of ctx.policy.middlewares.before) {
|
|
895
|
+
if (middleware._mode === "fluent") {
|
|
896
|
+
await middleware.func(ctx);
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
ctx = await middleware.func(ctx, {
|
|
900
|
+
abort: (reason) => userController.abort(reason),
|
|
901
|
+
throwError,
|
|
902
|
+
breakRetry
|
|
903
|
+
});
|
|
904
|
+
}
|
|
340
905
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
clearTimeout(timeoutTimer);
|
|
344
|
-
timeoutController.abort(new VigorRetryError("TIMED_OUT", {
|
|
906
|
+
mergedSignals.throwIfAborted();
|
|
907
|
+
timeoutId = setTimeout(() => timeoutController.abort(new VigorRetryError("TIMED_OUT", {
|
|
345
908
|
method: "request",
|
|
346
909
|
data: {
|
|
347
|
-
limit:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}));
|
|
351
|
-
}, stats.settings.timeout);
|
|
352
|
-
signal.throwIfAborted();
|
|
353
|
-
let onAbort;
|
|
354
|
-
try {
|
|
355
|
-
addTimeline("TARGET_REQUEST_STARTED", {
|
|
356
|
-
target: stats.target
|
|
357
|
-
});
|
|
358
|
-
const abort = (error) => {
|
|
359
|
-
addTimeline("TARGET_API_CALLED", {
|
|
360
|
-
target: stats.target,
|
|
361
|
-
method: "abort"
|
|
362
|
-
});
|
|
363
|
-
controller.abort(error);
|
|
364
|
-
throw error;
|
|
365
|
-
};
|
|
366
|
-
const started = performance.now();
|
|
910
|
+
limit: ctx.policy.settings.timeout
|
|
911
|
+
}
|
|
912
|
+
})), ctx.policy.settings.timeout);
|
|
367
913
|
ctx.result = await Promise.race([
|
|
368
|
-
|
|
914
|
+
target(mergedSignals),
|
|
369
915
|
new Promise((_, rej) => {
|
|
370
|
-
onAbort = () => rej(
|
|
371
|
-
|
|
916
|
+
onAbort = () => rej(mergedSignals.reason);
|
|
917
|
+
mergedSignals.addEventListener("abort", onAbort);
|
|
372
918
|
})
|
|
373
919
|
]);
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
920
|
+
for (const middleware of ctx.policy.middlewares.after) {
|
|
921
|
+
if (middleware._mode === "fluent") {
|
|
922
|
+
await middleware.func(ctx);
|
|
923
|
+
}
|
|
924
|
+
else {
|
|
925
|
+
ctx = await middleware.func(ctx, {
|
|
926
|
+
throwError
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const setResult = (res) => {
|
|
931
|
+
ctx.result = res;
|
|
932
|
+
};
|
|
933
|
+
for (const middleware of ctx.policy.middlewares.onResult) {
|
|
934
|
+
if (middleware._mode === "fluent") {
|
|
935
|
+
ctx.result = await middleware.func(ctx.result);
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
ctx = await middleware.func(ctx, {
|
|
939
|
+
setResult,
|
|
940
|
+
throwError
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return {
|
|
945
|
+
success: true,
|
|
946
|
+
data: ctx.result,
|
|
947
|
+
error: null
|
|
948
|
+
};
|
|
379
949
|
}
|
|
380
950
|
finally {
|
|
381
|
-
clearTimeout(
|
|
951
|
+
clearTimeout(timeoutId);
|
|
952
|
+
outerSignals.removeEventListener("abort", autoBreak);
|
|
382
953
|
if (onAbort)
|
|
383
|
-
|
|
954
|
+
mergedSignals.removeEventListener("abort", onAbort);
|
|
384
955
|
}
|
|
385
|
-
await handleInterceptor("after", (interceptorType, func) => ({
|
|
386
|
-
setResult: (unknown) => {
|
|
387
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
388
|
-
interceptorType,
|
|
389
|
-
interceptor: func,
|
|
390
|
-
method: "setResult",
|
|
391
|
-
args: [unknown]
|
|
392
|
-
});
|
|
393
|
-
ctx.result = unknown;
|
|
394
|
-
return unknown;
|
|
395
|
-
},
|
|
396
|
-
throwError: (error) => {
|
|
397
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
398
|
-
interceptorType,
|
|
399
|
-
interceptor: func,
|
|
400
|
-
method: "throwError",
|
|
401
|
-
args: [error]
|
|
402
|
-
});
|
|
403
|
-
throw error;
|
|
404
|
-
},
|
|
405
|
-
breakRetry: (error) => {
|
|
406
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
407
|
-
interceptorType,
|
|
408
|
-
interceptor: func,
|
|
409
|
-
method: "breakRetry",
|
|
410
|
-
args: [error]
|
|
411
|
-
});
|
|
412
|
-
ctx.flag.broke = true;
|
|
413
|
-
throw error;
|
|
414
|
-
},
|
|
415
|
-
}));
|
|
416
|
-
await handleInterceptor("result", (interceptorType, func) => ({
|
|
417
|
-
setResult: (unknown) => {
|
|
418
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
419
|
-
interceptorType,
|
|
420
|
-
interceptor: func,
|
|
421
|
-
method: "setResult",
|
|
422
|
-
args: [unknown]
|
|
423
|
-
});
|
|
424
|
-
ctx.result = unknown;
|
|
425
|
-
return unknown;
|
|
426
|
-
},
|
|
427
|
-
throwError: (error) => {
|
|
428
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
429
|
-
interceptorType,
|
|
430
|
-
interceptor: func,
|
|
431
|
-
method: "throwError",
|
|
432
|
-
args: [error]
|
|
433
|
-
});
|
|
434
|
-
throw error;
|
|
435
|
-
},
|
|
436
|
-
}));
|
|
437
|
-
return ctx.result;
|
|
438
956
|
}
|
|
439
957
|
catch (error) {
|
|
440
958
|
ctx.error = error;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
959
|
+
if (ctx.flags.brokeRetry)
|
|
960
|
+
throw ctx.error;
|
|
961
|
+
ctx.flags.doRetry = true;
|
|
962
|
+
const proceedRetry = () => ctx.flags.doRetry = true;
|
|
963
|
+
const cancelRetry = () => ctx.flags.doRetry = false;
|
|
964
|
+
for (const middleware of ctx.policy.middlewares.retryIf) {
|
|
965
|
+
if (middleware._mode === "fluent") {
|
|
966
|
+
ctx.flags.doRetry = await middleware.func(ctx.error);
|
|
445
967
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
await handleInterceptor("retryIf", (interceptorType, func) => ({
|
|
451
|
-
proceedRetry: () => {
|
|
452
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
453
|
-
interceptorType,
|
|
454
|
-
interceptor: func,
|
|
455
|
-
method: "proceedRetry",
|
|
456
|
-
args: []
|
|
457
|
-
});
|
|
458
|
-
return proceed = true;
|
|
459
|
-
},
|
|
460
|
-
cancelRetry: () => {
|
|
461
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
462
|
-
interceptorType,
|
|
463
|
-
interceptor: func,
|
|
464
|
-
method: "cancelRetry",
|
|
465
|
-
args: []
|
|
968
|
+
else {
|
|
969
|
+
ctx = await middleware.func(ctx, {
|
|
970
|
+
proceedRetry,
|
|
971
|
+
cancelRetry
|
|
466
972
|
});
|
|
467
|
-
return proceed = false;
|
|
468
973
|
}
|
|
469
|
-
}
|
|
470
|
-
if (!
|
|
471
|
-
throw error;
|
|
472
|
-
ctx.delay =
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
485
|
-
interceptorType,
|
|
486
|
-
interceptor: func,
|
|
487
|
-
method: "setDelay",
|
|
488
|
-
args: [number]
|
|
489
|
-
});
|
|
490
|
-
return ctx.delay = number;
|
|
491
|
-
},
|
|
492
|
-
setAttempt: (number) => {
|
|
493
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
494
|
-
interceptorType,
|
|
495
|
-
interceptor: func,
|
|
496
|
-
method: "setAttempt",
|
|
497
|
-
args: [number]
|
|
974
|
+
}
|
|
975
|
+
if (!ctx.flags.doRetry)
|
|
976
|
+
throw ctx.error;
|
|
977
|
+
ctx.system.delay = ctx.policy.algorithms._calculateDelay(ctx.system.attempt, ctx.policy.algorithms);
|
|
978
|
+
const setDelay = (num) => {
|
|
979
|
+
ctx.system.delay = num;
|
|
980
|
+
};
|
|
981
|
+
for (const middleware of ctx.policy.middlewares.onRetry) {
|
|
982
|
+
if (middleware._mode === "fluent") {
|
|
983
|
+
await middleware.func(ctx.error);
|
|
984
|
+
}
|
|
985
|
+
else {
|
|
986
|
+
ctx = await middleware.func(ctx, {
|
|
987
|
+
throwError,
|
|
988
|
+
setDelay
|
|
498
989
|
});
|
|
499
|
-
return ctx.attempt = number;
|
|
500
990
|
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const delay = ctx.delay;
|
|
505
|
-
await new Promise(r => setTimeout(r, delay));
|
|
991
|
+
}
|
|
992
|
+
const delay = ctx.system.delay;
|
|
993
|
+
await new Promise(res => setTimeout(res, delay));
|
|
506
994
|
}
|
|
507
995
|
}
|
|
508
|
-
throw new VigorRetryError("
|
|
996
|
+
throw new VigorRetryError("RETRY_EXHAUSTED", {
|
|
509
997
|
method: "request",
|
|
510
998
|
data: {
|
|
511
|
-
maxAttempts:
|
|
512
|
-
}
|
|
513
|
-
context: ctx
|
|
999
|
+
maxAttempts: ctx.policy.settings.maxAttempts
|
|
1000
|
+
}
|
|
514
1001
|
});
|
|
515
1002
|
}
|
|
516
1003
|
catch (error) {
|
|
517
1004
|
ctx.error = error;
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
1005
|
+
ctx.flags.doRestart = false;
|
|
1006
|
+
ctx.flags.overridden = false;
|
|
1007
|
+
const setResult = (res) => {
|
|
1008
|
+
ctx.flags.overridden = true;
|
|
1009
|
+
ctx.result = res;
|
|
1010
|
+
};
|
|
1011
|
+
const proceedRestart = () => ctx.flags.doRestart = true;
|
|
1012
|
+
const cancelRestart = () => ctx.flags.doRestart = false;
|
|
1013
|
+
for (const middleware of ctx.policy.middlewares.onError) {
|
|
1014
|
+
if (middleware._mode === "fluent") {
|
|
1015
|
+
await middleware.func(ctx.error);
|
|
522
1016
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
method: "setResult",
|
|
530
|
-
args: [unknown]
|
|
531
|
-
});
|
|
532
|
-
ctx.result = unknown;
|
|
533
|
-
ctx.flag.overwritten = true;
|
|
534
|
-
return unknown;
|
|
535
|
-
},
|
|
536
|
-
throwError: (error) => {
|
|
537
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
538
|
-
interceptorType,
|
|
539
|
-
interceptor: func,
|
|
540
|
-
method: "throwError",
|
|
541
|
-
args: [error]
|
|
542
|
-
});
|
|
543
|
-
throw error;
|
|
544
|
-
},
|
|
545
|
-
restart: () => {
|
|
546
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
547
|
-
interceptorType,
|
|
548
|
-
interceptor: func,
|
|
549
|
-
method: "restart",
|
|
550
|
-
args: []
|
|
1017
|
+
else {
|
|
1018
|
+
ctx = await middleware.func(ctx, {
|
|
1019
|
+
setResult,
|
|
1020
|
+
throwError,
|
|
1021
|
+
proceedRestart,
|
|
1022
|
+
cancelRestart
|
|
551
1023
|
});
|
|
552
|
-
ctx.flag.restarted = true;
|
|
553
1024
|
}
|
|
554
|
-
}));
|
|
555
|
-
if (ctx.flag.restarted) {
|
|
556
|
-
return await this.request(stats, ctx.timeline);
|
|
557
1025
|
}
|
|
558
|
-
if (ctx.
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
1026
|
+
if (ctx.flags.doRestart) {
|
|
1027
|
+
if (restartAttempt + 1 > ctx.policy.settings.maxRestarts)
|
|
1028
|
+
throw new VigorRetryError("RESTART_EXHAUSTED", {
|
|
1029
|
+
method: "request",
|
|
1030
|
+
data: {
|
|
1031
|
+
maxAttempts: ctx.policy.settings.maxRestarts
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
return await this.requestRuntime(restartAttempt + 1);
|
|
1035
|
+
}
|
|
1036
|
+
if (ctx.flags.overridden)
|
|
1037
|
+
return {
|
|
1038
|
+
success: true,
|
|
1039
|
+
data: ctx.result,
|
|
1040
|
+
error: null
|
|
1041
|
+
};
|
|
1042
|
+
if (!isVigorEmpty(ctx.policy.settings.default)) {
|
|
1043
|
+
const data = await ctx.policy.settings.default(ctx);
|
|
1044
|
+
return {
|
|
1045
|
+
success: true,
|
|
1046
|
+
data,
|
|
1047
|
+
error: null
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
return {
|
|
1051
|
+
success: false,
|
|
1052
|
+
data: null,
|
|
1053
|
+
error: ctx.error
|
|
1054
|
+
};
|
|
563
1055
|
}
|
|
564
1056
|
}
|
|
565
1057
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
1058
|
+
|
|
1059
|
+
const VigorParsePolicySettingsBase = {
|
|
1060
|
+
__brand: createBrand("Parse", "Policy<Settings<Schema"),
|
|
1061
|
+
raw: false,
|
|
1062
|
+
default: VigorEmpty,
|
|
1063
|
+
maxRestarts: 3
|
|
1064
|
+
};
|
|
1065
|
+
/** Immutable builder for a parse policy's general settings. */
|
|
1066
|
+
class VigorParsePolicySettings extends VigorStatus {
|
|
1067
|
+
constructor(config = VigorParseBase) {
|
|
1068
|
+
super(VigorParseBase, config);
|
|
1069
|
+
}
|
|
1070
|
+
_create(config) {
|
|
1071
|
+
return new VigorParsePolicySettings(config);
|
|
1072
|
+
}
|
|
1073
|
+
/** When enabled, skips content-type based parsing and returns the raw response. */
|
|
1074
|
+
raw(bool) {
|
|
1075
|
+
return this._next({
|
|
1076
|
+
policy: { settings: { raw: bool } }
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
/** Sets the fallback value/factory used when parsing ultimately fails. */
|
|
1080
|
+
default(func) {
|
|
1081
|
+
return this._next({
|
|
1082
|
+
policy: { settings: { default: func } }
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
/** Sets the maximum number of full restarts allowed. */
|
|
1086
|
+
maxRestarts(num) {
|
|
1087
|
+
return this._next({
|
|
1088
|
+
policy: { settings: { maxRestarts: num } }
|
|
1089
|
+
});
|
|
573
1090
|
}
|
|
574
|
-
original(bool) { return this._next({ raw: bool }); }
|
|
575
|
-
default(unk) { return this._next({ default: unk }); }
|
|
576
1091
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
{
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
1092
|
+
|
|
1093
|
+
const VigorParsePolicyMiddlewaresBase = {
|
|
1094
|
+
__brand: createBrand("Parse", "Policy<Middlewares<Schema"),
|
|
1095
|
+
before: [],
|
|
1096
|
+
after: [],
|
|
1097
|
+
onResult: [],
|
|
1098
|
+
onError: []
|
|
1099
|
+
};
|
|
1100
|
+
/**
|
|
1101
|
+
* Immutable builder for a parse policy's middleware pipeline (`before`,
|
|
1102
|
+
* `after`, `onResult`, `onError`). Each stage accepts either "fluent"
|
|
1103
|
+
* middleware (returns a plain value merged into the context) or
|
|
1104
|
+
* "intercept" middleware (receives an explicit API to mutate control flow).
|
|
1105
|
+
*/
|
|
1106
|
+
class VigorParsePolicyMiddlewares extends VigorStatus {
|
|
1107
|
+
constructor(config = VigorParseBase) {
|
|
1108
|
+
super(VigorParseBase, config);
|
|
1109
|
+
}
|
|
1110
|
+
_create(config) {
|
|
1111
|
+
return new VigorParsePolicyMiddlewares(config);
|
|
1112
|
+
}
|
|
1113
|
+
before(type, func) {
|
|
1114
|
+
return this._next({ policy: { middlewares: { before: [{ _mode: type, func }] } } });
|
|
1115
|
+
}
|
|
1116
|
+
after(type, func) {
|
|
1117
|
+
return this._next({ policy: { middlewares: { after: [{ _mode: type, func }] } } });
|
|
1118
|
+
}
|
|
1119
|
+
onResult(type, func) {
|
|
1120
|
+
return this._next({ policy: { middlewares: { onResult: [{ _mode: type, func }] } } });
|
|
1121
|
+
}
|
|
1122
|
+
onError(type, func) {
|
|
1123
|
+
return this._next({ policy: { middlewares: { onError: [{ _mode: type, func }] } } });
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const VigorParseErrorMessageFuncs = {
|
|
1128
|
+
VALUE_REQUIRED: ({ key }) => `Value Required (missing ${key})`,
|
|
1129
|
+
INVALID_CONTENT_TYPE: ({ received }) => `Invalid Content-Type header: ${String(received)}`,
|
|
1130
|
+
PARSER_NOT_FOUND: ({ received, expected }) => `Parser not found for Content-Type "${String(received)}" (known: ${expected.join(", ")})`,
|
|
1131
|
+
PARSER_ALL_FAILED: ({ tried }) => `All parsers failed, tried: ${tried.join(", ")}`,
|
|
1132
|
+
RESTART_EXHAUSTED: ({ maxAttempts }) => `Restart exhausted, (max ${maxAttempts})`,
|
|
1133
|
+
};
|
|
1134
|
+
/** Error type thrown by the parse module. */
|
|
1135
|
+
class VigorParseError extends VigorError {
|
|
1136
|
+
/**
|
|
1137
|
+
* @param code - One of the parse module's error codes.
|
|
1138
|
+
* @param options - Error metadata, including the `data` payload for `code`.
|
|
1139
|
+
*/
|
|
1140
|
+
constructor(code, options) {
|
|
1141
|
+
const messageFn = VigorParseErrorMessageFuncs[code];
|
|
1142
|
+
super(code, messageFn(options.data), options);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const VigorParsePolicyStrategiesBase = {
|
|
1147
|
+
__brand: createBrand("Parse", "Policy<Strategies<Schema"),
|
|
1148
|
+
funcs: []
|
|
1149
|
+
};
|
|
1150
|
+
const ContentTypeParsers = [
|
|
1151
|
+
{ header: "application/json", regExp: /application\/(.+\+)?json(.+\+)?/i, method: (res) => res.json() },
|
|
1152
|
+
{ header: "application/xml", regExp: /application\/(.+\+)?xml(.+\+)?/i, method: (res) => res.text() },
|
|
1153
|
+
{ header: "application/x-www-form-urlencoded", regExp: /application\/(.+\+)?x-www-form-urlencoded(.+\+)?/i, method: (res) => res.formData() },
|
|
1154
|
+
{ header: "application/octet-stream", regExp: /application\/(.+\+)?octet-stream(.+\+)?/i, method: (res) => res.arrayBuffer() },
|
|
1155
|
+
{ header: "image/*", regExp: /^image\/.+/i, method: (res) => res.blob() },
|
|
1156
|
+
{ header: "audio/*", regExp: /^audio\/.+/i, method: (res) => res.blob() },
|
|
1157
|
+
{ header: "video/*", regExp: /^video\/.+/i, method: (res) => res.blob() },
|
|
1158
|
+
{ header: "multipart/form-data", regExp: /multipart\/(.+\+)?form-data(.+\+)?/i, method: (res) => res.formData() },
|
|
1159
|
+
{ header: "text/*", regExp: /^text\/.+/i, method: (res) => res.text() },
|
|
1160
|
+
];
|
|
1161
|
+
const SniffMethods = [
|
|
1162
|
+
{ title: "json", method: (res) => res.json() },
|
|
1163
|
+
{ title: "formData", method: (res) => res.formData() },
|
|
1164
|
+
{ title: "text", method: (res) => res.text() },
|
|
1165
|
+
{ title: "blob", method: (res) => res.blob() },
|
|
1166
|
+
];
|
|
1167
|
+
/** Parses a response by matching its `Content-Type` header against a known parser table. */
|
|
1168
|
+
const VigorParseContentTypeStrategy = async (response) => {
|
|
1169
|
+
const contentTypeHeader = response.headers.get("content-type");
|
|
1170
|
+
if (!contentTypeHeader)
|
|
1171
|
+
throw new VigorParseError("INVALID_CONTENT_TYPE", {
|
|
1172
|
+
method: "VigorParseContentTypeStrategy",
|
|
1173
|
+
data: { received: contentTypeHeader }
|
|
1174
|
+
});
|
|
1175
|
+
const toDo = ContentTypeParsers.find(parser => parser.regExp.test(contentTypeHeader));
|
|
1176
|
+
if (!toDo)
|
|
1177
|
+
throw new VigorParseError("PARSER_NOT_FOUND", {
|
|
1178
|
+
method: "VigorParseContentTypeStrategy",
|
|
1179
|
+
data: {
|
|
1180
|
+
expected: ContentTypeParsers.map(p => p.header),
|
|
1181
|
+
received: contentTypeHeader
|
|
637
1182
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1183
|
+
});
|
|
1184
|
+
return await toDo.method(response);
|
|
1185
|
+
};
|
|
1186
|
+
/** Parses a response by trying a sequence of body-reading methods until one succeeds. */
|
|
1187
|
+
const VigorParseSniffStrategy = async (response) => {
|
|
1188
|
+
for (const [i, parser] of SniffMethods.entries()) {
|
|
1189
|
+
const cloned = (i === SniffMethods.length - 1) ? response : response.clone();
|
|
1190
|
+
try {
|
|
1191
|
+
return await parser.method(cloned);
|
|
645
1192
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
1193
|
+
catch { }
|
|
1194
|
+
}
|
|
1195
|
+
throw new VigorParseError("PARSER_ALL_FAILED", {
|
|
1196
|
+
method: "VigorParseSniffStrategy",
|
|
1197
|
+
data: { tried: SniffMethods.map(p => p.title) }
|
|
1198
|
+
});
|
|
1199
|
+
};
|
|
1200
|
+
/** Immutable builder for a parse policy's fallback chain of parsing strategies. */
|
|
1201
|
+
class VigorParsePolicyStrategies extends VigorStatus {
|
|
1202
|
+
constructor(config = VigorParseBase) {
|
|
1203
|
+
super(VigorParseBase, config);
|
|
1204
|
+
}
|
|
1205
|
+
_create(config) {
|
|
1206
|
+
return new VigorParsePolicyStrategies(config);
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Adds strategy functions to the fallback chain.
|
|
1210
|
+
*
|
|
1211
|
+
* @param replace - When `true`, replaces the existing chain instead of
|
|
1212
|
+
* appending to it (the default is to concat, since strategies are
|
|
1213
|
+
* tried in sequence as a fallback chain).
|
|
1214
|
+
*/
|
|
1215
|
+
_set(funcs, replace) {
|
|
1216
|
+
return this._next({ policy: { strategies: { funcs } } }, replace ? { policy: { strategies: { funcs: "replace" } } } : undefined);
|
|
665
1217
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
1218
|
+
/** Adds the content-type based parsing strategy. */
|
|
1219
|
+
contentType(replace) { return this._set([VigorParseContentTypeStrategy], replace); }
|
|
1220
|
+
/** Adds the sniffing (try-each-method) parsing strategy. */
|
|
1221
|
+
sniff(replace) { return this._set([VigorParseSniffStrategy], replace); }
|
|
1222
|
+
/** Adds a strategy that always parses the response as JSON. */
|
|
1223
|
+
json(replace) { return this._set([(res) => res.json()], replace); }
|
|
1224
|
+
/** Adds a strategy that always parses the response as text. */
|
|
1225
|
+
text(replace) { return this._set([(res) => res.text()], replace); }
|
|
1226
|
+
/** Adds a strategy that always parses the response as an `ArrayBuffer`. */
|
|
1227
|
+
arrayBuffer(replace) { return this._set([(res) => res.arrayBuffer()], replace); }
|
|
1228
|
+
/** Adds a strategy that always parses the response as a `Blob`. */
|
|
1229
|
+
blob(replace) { return this._set([(res) => res.blob()], replace); }
|
|
1230
|
+
/** Adds a strategy that always parses the response as a `Uint8Array`. */
|
|
1231
|
+
bytes(replace) { return this._set([(res) => res.arrayBuffer().then(b => new Uint8Array(b))], replace); }
|
|
1232
|
+
/** Adds a strategy that always parses the response as `FormData`. */
|
|
1233
|
+
formData(replace) { return this._set([(res) => res.formData()], replace); }
|
|
1234
|
+
/** Adds a user-supplied custom parsing strategy. */
|
|
1235
|
+
custom(func, replace) { return this._set([func], replace); }
|
|
670
1236
|
}
|
|
1237
|
+
|
|
1238
|
+
const VigorParsePolicyBase = {
|
|
1239
|
+
__brand: createBrand("Parse", "Policy<Schema"),
|
|
1240
|
+
target: VigorDefault,
|
|
1241
|
+
settings: VigorParsePolicySettingsBase,
|
|
1242
|
+
middlewares: VigorParsePolicyMiddlewaresBase,
|
|
1243
|
+
strategies: VigorParsePolicyStrategiesBase
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
const VigorParseContextBase = {
|
|
1247
|
+
__brand: createBrand("Parse", "Context<Schema"),
|
|
1248
|
+
result: VigorDefault,
|
|
1249
|
+
error: VigorDefault,
|
|
1250
|
+
response: VigorDefault,
|
|
1251
|
+
flags: {
|
|
1252
|
+
overrided: false,
|
|
1253
|
+
restarted: false
|
|
1254
|
+
},
|
|
1255
|
+
record: {},
|
|
1256
|
+
policy: VigorParsePolicyBase
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1259
|
+
const VigorParseBase = {
|
|
1260
|
+
__brand: createBrand("Parse", "Schema"),
|
|
1261
|
+
policy: VigorParsePolicyBase,
|
|
1262
|
+
context: VigorParseContextBase
|
|
1263
|
+
};
|
|
1264
|
+
/**
|
|
1265
|
+
* Immutable builder that parses an HTTP response into a result value,
|
|
1266
|
+
* trying each configured strategy in sequence with an optional middleware
|
|
1267
|
+
* pipeline and restart-on-failure support.
|
|
1268
|
+
*/
|
|
671
1269
|
class VigorParse extends VigorStatus {
|
|
672
|
-
constructor(config) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
interceptors: new VigorParseInterceptors()._getBase()
|
|
678
|
-
};
|
|
679
|
-
super(config, base, (c) => new VigorParse(c));
|
|
680
|
-
}
|
|
681
|
-
_createTimelineHandler(timeline) {
|
|
682
|
-
return (action, content) => {
|
|
683
|
-
timeline.push({
|
|
684
|
-
action: action,
|
|
685
|
-
content: content,
|
|
686
|
-
time: Date.now()
|
|
687
|
-
});
|
|
688
|
-
};
|
|
1270
|
+
constructor(config = VigorParseBase) {
|
|
1271
|
+
super(VigorParseBase, config);
|
|
1272
|
+
}
|
|
1273
|
+
_create(config) {
|
|
1274
|
+
return new VigorParse(config);
|
|
689
1275
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const interceptors = interceptorsConfig[interceptorType];
|
|
694
|
-
addTimeline("INTERCEPTOR_LOOP_STARTED", {
|
|
695
|
-
interceptorType: interceptorType,
|
|
696
|
-
interceptors,
|
|
697
|
-
});
|
|
698
|
-
const startTime = performance.now();
|
|
699
|
-
for (const func of interceptors) {
|
|
700
|
-
const scopedApi = api(interceptorType, func);
|
|
701
|
-
await func(ctx, scopedApi);
|
|
702
|
-
}
|
|
703
|
-
const endTime = performance.now();
|
|
704
|
-
addTimeline("INTERCEPTOR_LOOP_ENDED", {
|
|
705
|
-
interceptorType: interceptorType,
|
|
706
|
-
interceptors,
|
|
707
|
-
took: endTime - startTime
|
|
708
|
-
});
|
|
709
|
-
};
|
|
1276
|
+
/** Sets the `Response` object to parse. */
|
|
1277
|
+
target(response) {
|
|
1278
|
+
return this._next({ policy: { target: response } });
|
|
710
1279
|
}
|
|
711
|
-
|
|
712
|
-
settings(
|
|
713
|
-
if (
|
|
714
|
-
|
|
1280
|
+
/** Configures the parse policy's general settings. */
|
|
1281
|
+
settings(input) {
|
|
1282
|
+
if (typeof input === 'function' || input instanceof VigorParsePolicySettings) {
|
|
1283
|
+
const result = typeof input === 'function'
|
|
1284
|
+
? input(new VigorParsePolicySettings())
|
|
1285
|
+
: input;
|
|
1286
|
+
return this._next(result.config);
|
|
715
1287
|
}
|
|
716
|
-
|
|
717
|
-
return this._next({ settings: func(new VigorParseSettings(this._config.settings))._getConfig() });
|
|
718
|
-
}
|
|
719
|
-
return this._next({ settings: func });
|
|
1288
|
+
return this._next({ policy: { settings: input } });
|
|
720
1289
|
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
1290
|
+
/** Configures the parse policy's middleware pipeline. */
|
|
1291
|
+
middlewares(input) {
|
|
1292
|
+
if (typeof input === 'function' || input instanceof VigorParsePolicyMiddlewares) {
|
|
1293
|
+
const result = typeof input === 'function'
|
|
1294
|
+
? input(new VigorParsePolicyMiddlewares())
|
|
1295
|
+
: input;
|
|
1296
|
+
return this._next(result.config);
|
|
727
1297
|
}
|
|
728
|
-
return this._next({
|
|
1298
|
+
return this._next({ policy: { middlewares: input } });
|
|
729
1299
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
1300
|
+
/** Configures the parse policy's fallback chain of parsing strategies. */
|
|
1301
|
+
strategies(input) {
|
|
1302
|
+
if (typeof input === 'function' || input instanceof VigorParsePolicyStrategies) {
|
|
1303
|
+
const result = typeof input === 'function'
|
|
1304
|
+
? input(new VigorParsePolicyStrategies())
|
|
1305
|
+
: input;
|
|
1306
|
+
return this._next(result.config);
|
|
733
1307
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1308
|
+
return this._next({ policy: { strategies: input } });
|
|
1309
|
+
}
|
|
1310
|
+
/** Runs parsing and returns the result directly, throwing on final failure. */
|
|
1311
|
+
async request() {
|
|
1312
|
+
const res = await this.requestVerbose();
|
|
1313
|
+
if (res.success)
|
|
1314
|
+
return res.data;
|
|
1315
|
+
throw res.error;
|
|
738
1316
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1317
|
+
/** Runs parsing and returns a result/error envelope instead of throwing. */
|
|
1318
|
+
async requestVerbose() {
|
|
1319
|
+
return await this.requestRuntime();
|
|
1320
|
+
}
|
|
1321
|
+
/** Internal execution loop implementing the strategy fallback and restart handling. */
|
|
1322
|
+
async requestRuntime(restartAttempt = 1) {
|
|
1323
|
+
const config = this._merge(this.config, {});
|
|
742
1324
|
let ctx = {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
response: target,
|
|
746
|
-
result: VigorDefault,
|
|
747
|
-
error: VigorDefault,
|
|
748
|
-
flag: {
|
|
749
|
-
overwritten: false
|
|
750
|
-
}
|
|
1325
|
+
...config.context,
|
|
1326
|
+
policy: config.policy
|
|
751
1327
|
};
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1328
|
+
if (isVigorDefault(ctx.policy.target))
|
|
1329
|
+
throw new VigorParseError("VALUE_REQUIRED", {
|
|
1330
|
+
method: "request",
|
|
1331
|
+
data: { key: "target" },
|
|
1332
|
+
context: ctx
|
|
1333
|
+
});
|
|
1334
|
+
ctx.response = ctx.policy.target;
|
|
1335
|
+
const throwError = (err) => { throw err; };
|
|
758
1336
|
try {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
771
|
-
interceptorType,
|
|
772
|
-
interceptor: func,
|
|
773
|
-
method: "throwError",
|
|
774
|
-
args: [error]
|
|
775
|
-
});
|
|
776
|
-
throw error;
|
|
777
|
-
},
|
|
778
|
-
}));
|
|
779
|
-
if (stats.settings.raw) {
|
|
780
|
-
ctx.result = ctx.response;
|
|
1337
|
+
for (const middleware of ctx.policy.middlewares.before) {
|
|
1338
|
+
if (middleware._mode === "fluent") {
|
|
1339
|
+
await middleware.func(ctx);
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
ctx = await middleware.func(ctx, { throwError });
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
const response = ctx.response;
|
|
1346
|
+
if (ctx.policy.settings.raw) {
|
|
1347
|
+
ctx.result = response;
|
|
781
1348
|
}
|
|
782
1349
|
else {
|
|
1350
|
+
const funcs = ctx.policy.strategies.funcs.length > 0
|
|
1351
|
+
? ctx.policy.strategies.funcs
|
|
1352
|
+
: [VigorParseContentTypeStrategy];
|
|
783
1353
|
let parsed = false;
|
|
784
|
-
for (const [i, func] of
|
|
785
|
-
?
|
|
786
|
-
: new VigorParseStrategies().contentType()._getConfig().funcs.entries()) {
|
|
787
|
-
const cloned = (i === stats.strategies.funcs.length - 1)
|
|
788
|
-
? ctx.response
|
|
789
|
-
: ctx.response.clone();
|
|
1354
|
+
for (const [i, func] of funcs.entries()) {
|
|
1355
|
+
const cloned = (i === funcs.length - 1) ? response : response.clone();
|
|
790
1356
|
try {
|
|
791
1357
|
ctx.result = await func(cloned);
|
|
792
1358
|
parsed = true;
|
|
@@ -797,953 +1363,583 @@ class VigorParse extends VigorStatus {
|
|
|
797
1363
|
if (!parsed)
|
|
798
1364
|
throw new VigorParseError("PARSER_ALL_FAILED", {
|
|
799
1365
|
method: "request",
|
|
800
|
-
data: {
|
|
801
|
-
tried: stats.strategies.funcs,
|
|
802
|
-
response: ctx.response
|
|
803
|
-
},
|
|
1366
|
+
data: { tried: funcs.map((_, i) => `strategy#${i}`) },
|
|
804
1367
|
context: ctx
|
|
805
1368
|
});
|
|
806
1369
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
setResult: (unknown) => {
|
|
830
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
831
|
-
interceptorType,
|
|
832
|
-
interceptor: func,
|
|
833
|
-
method: "setResult",
|
|
834
|
-
args: [unknown]
|
|
835
|
-
});
|
|
836
|
-
ctx.result = unknown;
|
|
837
|
-
return unknown;
|
|
838
|
-
},
|
|
839
|
-
throwError: (error) => {
|
|
840
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
841
|
-
interceptorType,
|
|
842
|
-
interceptor: func,
|
|
843
|
-
method: "throwError",
|
|
844
|
-
args: [error]
|
|
845
|
-
});
|
|
846
|
-
throw error;
|
|
847
|
-
},
|
|
848
|
-
}));
|
|
849
|
-
return ctx.result;
|
|
1370
|
+
const setResult = (res) => { ctx.result = res; };
|
|
1371
|
+
for (const middleware of ctx.policy.middlewares.after) {
|
|
1372
|
+
if (middleware._mode === "fluent") {
|
|
1373
|
+
await middleware.func(ctx);
|
|
1374
|
+
}
|
|
1375
|
+
else {
|
|
1376
|
+
ctx = await middleware.func(ctx, { setResult, throwError });
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
for (const middleware of ctx.policy.middlewares.onResult) {
|
|
1380
|
+
if (middleware._mode === "fluent") {
|
|
1381
|
+
ctx.result = await middleware.func(ctx.result);
|
|
1382
|
+
}
|
|
1383
|
+
else {
|
|
1384
|
+
ctx = await middleware.func(ctx, { setResult, throwError });
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
return {
|
|
1388
|
+
success: true,
|
|
1389
|
+
data: ctx.result,
|
|
1390
|
+
error: null
|
|
1391
|
+
};
|
|
850
1392
|
}
|
|
851
1393
|
catch (error) {
|
|
852
1394
|
ctx.error = error;
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1395
|
+
ctx.flags.overrided = false;
|
|
1396
|
+
ctx.flags.restarted = false;
|
|
1397
|
+
const setResult = (res) => {
|
|
1398
|
+
ctx.flags.overrided = true;
|
|
1399
|
+
ctx.result = res;
|
|
1400
|
+
};
|
|
1401
|
+
const proceedRestart = () => ctx.flags.restarted = true;
|
|
1402
|
+
const cancelRestart = () => ctx.flags.restarted = false;
|
|
1403
|
+
for (const middleware of ctx.policy.middlewares.onError) {
|
|
1404
|
+
if (middleware._mode === "fluent") {
|
|
1405
|
+
await middleware.func(ctx.error);
|
|
857
1406
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
return unknown;
|
|
870
|
-
},
|
|
871
|
-
throwError: (error) => {
|
|
872
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
873
|
-
interceptorType,
|
|
874
|
-
interceptor: func,
|
|
875
|
-
method: "throwError",
|
|
876
|
-
args: [error]
|
|
1407
|
+
else {
|
|
1408
|
+
ctx = await middleware.func(ctx, { setResult, throwError, proceedRestart, cancelRestart });
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
if (ctx.flags.restarted) {
|
|
1412
|
+
if (restartAttempt + 1 > ctx.policy.settings.maxRestarts)
|
|
1413
|
+
throw new VigorParseError("RESTART_EXHAUSTED", {
|
|
1414
|
+
method: "request",
|
|
1415
|
+
data: {
|
|
1416
|
+
maxAttempts: ctx.policy.settings.maxRestarts
|
|
1417
|
+
}
|
|
877
1418
|
});
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1419
|
+
return await this.requestRuntime(restartAttempt + 1);
|
|
1420
|
+
}
|
|
1421
|
+
if (ctx.flags.overrided)
|
|
1422
|
+
return {
|
|
1423
|
+
success: true,
|
|
1424
|
+
data: ctx.result,
|
|
1425
|
+
error: null
|
|
1426
|
+
};
|
|
1427
|
+
if (!isVigorEmpty(ctx.policy.settings.default)) {
|
|
1428
|
+
const data = await ctx.policy.settings.default(ctx);
|
|
1429
|
+
return {
|
|
1430
|
+
success: true,
|
|
1431
|
+
data,
|
|
1432
|
+
error: null
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
return {
|
|
1436
|
+
success: false,
|
|
1437
|
+
data: null,
|
|
1438
|
+
error: ctx.error
|
|
1439
|
+
};
|
|
886
1440
|
}
|
|
887
1441
|
}
|
|
888
1442
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1443
|
+
|
|
1444
|
+
const VigorFetchPolicyBase = {
|
|
1445
|
+
__brand: createBrand("Fetch", "Policy<Schema"),
|
|
1446
|
+
method: VigorEmpty,
|
|
1447
|
+
origin: VigorEmpty,
|
|
1448
|
+
path: [],
|
|
1449
|
+
query: [],
|
|
1450
|
+
hash: "",
|
|
1451
|
+
headers: {},
|
|
1452
|
+
body: VigorEmpty,
|
|
1453
|
+
extra: {},
|
|
1454
|
+
settings: VigorFetchPolicySettingsBase,
|
|
1455
|
+
middlewares: VigorFetchPolicyMiddlewaresBase,
|
|
1456
|
+
retry: VigorRetryBase,
|
|
1457
|
+
parse: VigorParseBase
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1460
|
+
const VigorFetchContextBase = {
|
|
1461
|
+
__brand: createBrand("Fetch", "Context<Schema"),
|
|
1462
|
+
href: "",
|
|
1463
|
+
response: VigorDefault,
|
|
1464
|
+
result: VigorDefault,
|
|
1465
|
+
error: VigorDefault,
|
|
1466
|
+
options: VigorDefault,
|
|
1467
|
+
flags: {
|
|
1468
|
+
overrided: false,
|
|
1469
|
+
restarted: false
|
|
1470
|
+
},
|
|
1471
|
+
record: {},
|
|
1472
|
+
policy: VigorFetchPolicyBase
|
|
1473
|
+
};
|
|
1474
|
+
|
|
1475
|
+
const VigorFetchErrorMessageFuncs = {
|
|
1476
|
+
VALUE_REQUIRED: ({ key }) => `Value Required (missing ${key})`,
|
|
1477
|
+
INVALID_BODY: ({ received }) => `Invalid Body type: ${typeof received}`,
|
|
1478
|
+
INVALID_PROTOCOL: ({ origin, base }) => base
|
|
1479
|
+
? `Invalid URL: could not resolve "${origin}" against base "${base}"`
|
|
1480
|
+
: `Invalid URL: "${origin}" is not an absolute URL, and no base (window.location) is available to resolve it against — pass an absolute origin (e.g. "https://api.example.com") when running outside a browser`,
|
|
1481
|
+
FETCH_FAILED: ({ status, statusText }) => `Fetch Failed: ${status} ${statusText}`,
|
|
1482
|
+
RESTART_EXHAUSTED: ({ maxAttempts }) => `Restart exhausted, (max ${maxAttempts})`,
|
|
1483
|
+
};
|
|
1484
|
+
/** Error type thrown by the fetch module. */
|
|
1485
|
+
class VigorFetchError extends VigorError {
|
|
1486
|
+
/**
|
|
1487
|
+
* @param code - One of the fetch module's error codes.
|
|
1488
|
+
* @param options - Error metadata, including the `data` payload for `code`.
|
|
1489
|
+
*/
|
|
1490
|
+
constructor(code, options) {
|
|
1491
|
+
const messageFn = VigorFetchErrorMessageFuncs[code];
|
|
1492
|
+
super(code, messageFn(options.data), options);
|
|
911
1493
|
}
|
|
912
|
-
before(...funcs) { return this._next({ before: funcs.flat() }); }
|
|
913
|
-
after(...funcs) { return this._next({ after: funcs.flat() }); }
|
|
914
|
-
result(...funcs) { return this._next({ result: funcs.flat() }); }
|
|
915
|
-
onError(...funcs) { return this._next({ onError: funcs.flat() }); }
|
|
916
1494
|
}
|
|
1495
|
+
|
|
1496
|
+
const VigorFetchBase = {
|
|
1497
|
+
__brand: createBrand("Fetch", "Schema"),
|
|
1498
|
+
policy: VigorFetchPolicyBase,
|
|
1499
|
+
context: VigorFetchContextBase
|
|
1500
|
+
};
|
|
1501
|
+
/**
|
|
1502
|
+
* Immutable builder that performs an HTTP request, composing an internal
|
|
1503
|
+
* {@link VigorRetry} engine (for retry/timeout/backoff) and a
|
|
1504
|
+
* {@link VigorParse} engine (for response parsing), with a configurable
|
|
1505
|
+
* middleware pipeline around request building and response handling.
|
|
1506
|
+
*/
|
|
917
1507
|
class VigorFetch extends VigorStatus {
|
|
918
|
-
constructor(config) {
|
|
919
|
-
|
|
920
|
-
origin: VigorDefault,
|
|
921
|
-
path: [],
|
|
922
|
-
query: [],
|
|
923
|
-
hash: "",
|
|
924
|
-
options: {
|
|
925
|
-
headers: {},
|
|
926
|
-
body: VigorDefault
|
|
927
|
-
},
|
|
928
|
-
settings: new VigorFetchSettings()._getBase(),
|
|
929
|
-
interceptors: new VigorFetchInterceptors()._getBase(),
|
|
930
|
-
retryConfig: new VigorRetry()._getBase(),
|
|
931
|
-
parseConfig: new VigorParse()._getBase()
|
|
932
|
-
};
|
|
933
|
-
super(config, base, (c) => new VigorFetch(c));
|
|
934
|
-
}
|
|
935
|
-
_createTimelineHandler(timeline) {
|
|
936
|
-
return (action, content) => {
|
|
937
|
-
timeline.push({
|
|
938
|
-
action: action,
|
|
939
|
-
content: content,
|
|
940
|
-
time: Date.now()
|
|
941
|
-
});
|
|
942
|
-
};
|
|
1508
|
+
constructor(config = VigorFetchBase) {
|
|
1509
|
+
super(VigorFetchBase, config);
|
|
943
1510
|
}
|
|
944
|
-
|
|
945
|
-
return
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1511
|
+
_create(config) {
|
|
1512
|
+
return new VigorFetch(config);
|
|
1513
|
+
}
|
|
1514
|
+
/** Sets the HTTP method. Defaults to `POST` when a body is set, otherwise `GET`. */
|
|
1515
|
+
method(str) { return this._next({ policy: { method: str } }); }
|
|
1516
|
+
/** Sets the request origin, either an absolute URL or a path relative to the current page. */
|
|
1517
|
+
origin(str) { return this._next({ policy: { origin: str } }); }
|
|
1518
|
+
/** Appends path segments to the request URL. */
|
|
1519
|
+
path(...strs) {
|
|
1520
|
+
return this._next({ policy: { path: this._stringifyList(strs) } });
|
|
1521
|
+
}
|
|
1522
|
+
/** Adds query-string parameter objects to the request URL. */
|
|
1523
|
+
query(...objs) {
|
|
1524
|
+
return this._next({ policy: { query: objs } });
|
|
1525
|
+
}
|
|
1526
|
+
/** Sets the URL fragment ("hash"), replacing any previous value. */
|
|
1527
|
+
hash(str) { return this._next({ policy: { hash: str } }, { policy: { hash: "replace" } }); }
|
|
1528
|
+
/**
|
|
1529
|
+
* Sets request headers.
|
|
1530
|
+
*
|
|
1531
|
+
* @param mode - `"overwrite"` discards all previously configured headers
|
|
1532
|
+
* and keeps only the ones passed here. `"merge"` behaves like
|
|
1533
|
+
* `Object.assign`, overwriting only the keys that overlap and keeping
|
|
1534
|
+
* the rest.
|
|
1535
|
+
*/
|
|
1536
|
+
headers(mode, obj) {
|
|
1537
|
+
if (mode === "overwrite")
|
|
1538
|
+
return this._next({ policy: { headers: obj } }, { policy: { headers: "replace" } });
|
|
1539
|
+
return this._next({ policy: { headers: obj } });
|
|
964
1540
|
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Sets the request body.
|
|
1543
|
+
*
|
|
1544
|
+
* @param mode - `"overwrite"` discards the previous body and fully
|
|
1545
|
+
* replaces it with this value. `"merge"` shallow-merges (via
|
|
1546
|
+
* `Object.assign`) when both the previous and new body are plain
|
|
1547
|
+
* objects; for any other body type (string/Blob/FormData/etc) it is
|
|
1548
|
+
* simply replaced.
|
|
1549
|
+
*/
|
|
1550
|
+
body(mode, obj) {
|
|
1551
|
+
if (mode === "overwrite")
|
|
1552
|
+
return this._next({ policy: { body: obj } }, { policy: { body: "replace" } });
|
|
1553
|
+
return this._next({ policy: { body: obj } });
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Sets additional `RequestInit` options other than headers/body/method/signal
|
|
1557
|
+
* (e.g. `credentials`, `mode`, `cache`, `redirect`, `referrer`,
|
|
1558
|
+
* `referrerPolicy`, `keepalive`, `integrity`).
|
|
1559
|
+
*
|
|
1560
|
+
* @param mode - `"overwrite"` discards all previously configured options
|
|
1561
|
+
* and replaces them with this value. `"merge"` shallow-merges this
|
|
1562
|
+
* value onto the previous options.
|
|
1563
|
+
*/
|
|
1564
|
+
options(mode, obj) {
|
|
1565
|
+
if (mode === "overwrite")
|
|
1566
|
+
return this._next({ policy: { extra: obj } }, { policy: { extra: "replace" } });
|
|
1567
|
+
return this._next({ policy: { extra: obj } });
|
|
1568
|
+
}
|
|
1569
|
+
/** Configures the fetch policy's general settings. */
|
|
1570
|
+
settings(input) {
|
|
1571
|
+
if (typeof input === 'function' || input instanceof VigorFetchPolicySettings) {
|
|
1572
|
+
const result = typeof input === 'function'
|
|
1573
|
+
? input(new VigorFetchPolicySettings())
|
|
1574
|
+
: input;
|
|
1575
|
+
return this._next(result.config);
|
|
1576
|
+
}
|
|
1577
|
+
return this._next({ policy: { settings: input } });
|
|
1578
|
+
}
|
|
1579
|
+
/** Configures the fetch policy's middleware pipeline. */
|
|
1580
|
+
middlewares(input) {
|
|
1581
|
+
if (typeof input === 'function' || input instanceof VigorFetchPolicyMiddlewares) {
|
|
1582
|
+
const result = typeof input === 'function'
|
|
1583
|
+
? input(new VigorFetchPolicyMiddlewares())
|
|
1584
|
+
: input;
|
|
1585
|
+
return this._next(result.config);
|
|
1586
|
+
}
|
|
1587
|
+
return this._next({ policy: { middlewares: input } });
|
|
1588
|
+
}
|
|
1589
|
+
/** Configures the retry engine used to send the underlying request. */
|
|
1590
|
+
retry(input) {
|
|
1591
|
+
if (typeof input === 'function' || input instanceof VigorRetry) {
|
|
1592
|
+
const result = typeof input === 'function'
|
|
1593
|
+
? input(new VigorRetry())
|
|
1594
|
+
: input;
|
|
1595
|
+
return this._next({ policy: { retry: result.config } }, { policy: { retry: "replace" } });
|
|
1596
|
+
}
|
|
1597
|
+
return this._next({ policy: { retry: input } }, { policy: { retry: "replace" } });
|
|
1598
|
+
}
|
|
1599
|
+
/** Configures the parse engine used to interpret the response. */
|
|
1600
|
+
parse(input) {
|
|
1601
|
+
if (typeof input === 'function' || input instanceof VigorParse) {
|
|
1602
|
+
const result = typeof input === 'function'
|
|
1603
|
+
? input(new VigorParse())
|
|
1604
|
+
: input;
|
|
1605
|
+
return this._next({ policy: { parse: result.config } }, { policy: { parse: "replace" } });
|
|
1606
|
+
}
|
|
1607
|
+
return this._next({ policy: { parse: input } }, { policy: { parse: "replace" } });
|
|
1608
|
+
}
|
|
1609
|
+
/** Converts a list of stringable values into strings, dropping `null`/`undefined` entries. */
|
|
965
1610
|
_stringifyList(unkList) {
|
|
966
1611
|
return unkList
|
|
967
1612
|
.filter(unk => unk !== null && unk !== undefined)
|
|
968
|
-
.map(unk =>
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1613
|
+
.map(unk => unk instanceof Date ? unk.toISOString() : String(unk));
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Resolves the base URL used to interpret a relative `origin`. In a
|
|
1617
|
+
* browser environment this is the current page location; in
|
|
1618
|
+
* environments without `location` (e.g. Node), no base is available and
|
|
1619
|
+
* `origin` must be an absolute URL.
|
|
1620
|
+
*/
|
|
1621
|
+
_resolveBase() {
|
|
1622
|
+
if (typeof globalThis !== 'undefined' && globalThis.location?.href) {
|
|
1623
|
+
return globalThis.location.href;
|
|
1624
|
+
}
|
|
1625
|
+
return undefined;
|
|
973
1626
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
headers(obj) { return this._next({ options: { headers: obj } }); }
|
|
981
|
-
body(obj) { return this._next({ options: { headers: this._config.options.headers, body: obj } }); }
|
|
1627
|
+
/**
|
|
1628
|
+
* Builds the final request URL from the configured origin, path
|
|
1629
|
+
* segments, query parameters, and hash. An absolute `origin` ignores
|
|
1630
|
+
* the resolved base (per the `URL` spec); a relative `origin` is
|
|
1631
|
+
* resolved against it. Throws `INVALID_PROTOCOL` if neither is possible.
|
|
1632
|
+
*/
|
|
982
1633
|
_buildUrl(origin, path, query, hash) {
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1634
|
+
const base = this._resolveBase();
|
|
1635
|
+
let originObj;
|
|
1636
|
+
try {
|
|
1637
|
+
originObj = new URL(origin, base);
|
|
1638
|
+
}
|
|
1639
|
+
catch {
|
|
1640
|
+
throw new VigorFetchError("INVALID_PROTOCOL", {
|
|
1641
|
+
method: "_buildUrl",
|
|
1642
|
+
data: { origin, base }
|
|
1643
|
+
});
|
|
988
1644
|
}
|
|
989
|
-
const
|
|
1645
|
+
const baseStr = originObj.origin;
|
|
1646
|
+
const pathParts = [originObj.pathname.replace(/^\/+|\/+$/g, '')];
|
|
1647
|
+
for (const p of path)
|
|
1648
|
+
pathParts.push(p.replace(/^\/+|\/+$/g, ''));
|
|
1649
|
+
const pathStr = pathParts.filter(Boolean).join('/');
|
|
990
1650
|
const mainObj = new URL(pathStr, baseStr);
|
|
991
|
-
const parseVal = (val) =>
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
return String(val);
|
|
995
|
-
};
|
|
996
|
-
const queryObj = [...Array.from(originObj.searchParams.entries()), ...query.flatMap(qu => Object.entries(qu))];
|
|
997
|
-
for (const [key, val] of queryObj) {
|
|
1651
|
+
const parseVal = (val) => val instanceof Date ? val.toISOString() : String(val);
|
|
1652
|
+
const queryEntries = [...originObj.searchParams.entries(), ...query.flatMap(q => Object.entries(q))];
|
|
1653
|
+
for (const [key, val] of queryEntries) {
|
|
998
1654
|
if (val === undefined || val === null)
|
|
999
1655
|
continue;
|
|
1000
|
-
if (Array.isArray(val))
|
|
1001
|
-
for (const e of val)
|
|
1656
|
+
if (Array.isArray(val)) {
|
|
1657
|
+
for (const e of val)
|
|
1002
1658
|
mainObj.searchParams.append(key, parseVal(e));
|
|
1003
|
-
|
|
1659
|
+
}
|
|
1004
1660
|
else {
|
|
1005
1661
|
mainObj.searchParams.append(key, parseVal(val));
|
|
1006
1662
|
}
|
|
1007
1663
|
}
|
|
1008
|
-
mainObj.hash = hash
|
|
1664
|
+
mainObj.hash = hash || originObj.hash;
|
|
1009
1665
|
return mainObj.href;
|
|
1010
1666
|
}
|
|
1011
|
-
|
|
1667
|
+
/** Infers the `Content-Type` header and serializes `body` into a `BodyInit`. */
|
|
1668
|
+
_normalizeBody(body) {
|
|
1012
1669
|
if (body == null)
|
|
1013
|
-
return {
|
|
1670
|
+
return { headers: {}, body: body };
|
|
1014
1671
|
if (typeof body === "string")
|
|
1015
|
-
return {
|
|
1016
|
-
"Content-Type": "text/plain;charset=UTF-8"
|
|
1017
|
-
}, body };
|
|
1672
|
+
return { headers: { "Content-Type": "text/plain;charset=UTF-8" }, body };
|
|
1018
1673
|
if (body instanceof Blob)
|
|
1019
|
-
return {
|
|
1020
|
-
...(body.type && { "Content-Type": body.type })
|
|
1021
|
-
}, body };
|
|
1674
|
+
return { headers: body.type ? { "Content-Type": body.type } : {}, body };
|
|
1022
1675
|
if (body instanceof ArrayBuffer)
|
|
1023
|
-
return {
|
|
1024
|
-
"Content-Type": "application/octet-stream"
|
|
1025
|
-
}, body };
|
|
1676
|
+
return { headers: { "Content-Type": "application/octet-stream" }, body };
|
|
1026
1677
|
if (body instanceof URLSearchParams)
|
|
1027
|
-
return {
|
|
1028
|
-
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
|
|
1029
|
-
}, body };
|
|
1678
|
+
return { headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" }, body };
|
|
1030
1679
|
if (body instanceof FormData)
|
|
1031
|
-
return {
|
|
1032
|
-
if (typeof body === "object")
|
|
1033
|
-
return {
|
|
1034
|
-
"Content-Type": "application/json"
|
|
1035
|
-
}, body: JSON.stringify(body) };
|
|
1036
|
-
}
|
|
1680
|
+
return { headers: {}, body };
|
|
1681
|
+
if (typeof body === "object")
|
|
1682
|
+
return { headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) };
|
|
1037
1683
|
throw new VigorFetchError("INVALID_BODY", {
|
|
1038
1684
|
method: "_normalizeBody",
|
|
1039
|
-
data: {
|
|
1040
|
-
expected: ["string", "Blob", "ArrayBuffer", "URLSearchParams", "FormData"],
|
|
1041
|
-
received: body
|
|
1042
|
-
}
|
|
1685
|
+
data: { received: body }
|
|
1043
1686
|
});
|
|
1044
1687
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
}
|
|
1052
|
-
return this._next({ settings: func });
|
|
1053
|
-
}
|
|
1054
|
-
interceptors(func) {
|
|
1055
|
-
if (func instanceof VigorFetchInterceptors) {
|
|
1056
|
-
return this._next({ interceptors: func._getConfig() });
|
|
1057
|
-
}
|
|
1058
|
-
if (typeof func === 'function') {
|
|
1059
|
-
return this._next({ interceptors: func(new VigorFetchInterceptors(this._config.interceptors))._getConfig() });
|
|
1060
|
-
}
|
|
1061
|
-
return this._next({ interceptors: func });
|
|
1062
|
-
}
|
|
1063
|
-
retryConfig(func) {
|
|
1064
|
-
if (func instanceof VigorRetry) {
|
|
1065
|
-
return this._next({ retryConfig: func._getConfig() });
|
|
1066
|
-
}
|
|
1067
|
-
if (typeof func === 'function') {
|
|
1068
|
-
return this._next({ retryConfig: func(new VigorRetry(this._config.retryConfig))._getConfig() });
|
|
1069
|
-
}
|
|
1070
|
-
return this._next({ retryConfig: func });
|
|
1688
|
+
/** Runs the request and returns its parsed result directly, throwing on final failure. */
|
|
1689
|
+
async request() {
|
|
1690
|
+
const res = await this.requestVerbose();
|
|
1691
|
+
if (res.success)
|
|
1692
|
+
return res.data;
|
|
1693
|
+
throw res.error;
|
|
1071
1694
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
}
|
|
1076
|
-
if (typeof func === 'function') {
|
|
1077
|
-
return this._next({ parseConfig: func(new VigorParse(this._config.parseConfig))._getConfig() });
|
|
1078
|
-
}
|
|
1079
|
-
return this._next({ parseConfig: func });
|
|
1695
|
+
/** Runs the request and returns a result/error envelope instead of throwing. */
|
|
1696
|
+
async requestVerbose() {
|
|
1697
|
+
return await this.requestRuntime();
|
|
1080
1698
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1699
|
+
/** Internal execution loop implementing URL building, retry, parsing, and restart handling. */
|
|
1700
|
+
async requestRuntime(restartAttempt = 1) {
|
|
1701
|
+
const config = this._merge(this.config, {});
|
|
1083
1702
|
let ctx = {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
response: VigorDefault,
|
|
1087
|
-
options: {
|
|
1088
|
-
headers: VigorDefault,
|
|
1089
|
-
body: VigorDefault
|
|
1090
|
-
},
|
|
1091
|
-
error: VigorDefault,
|
|
1092
|
-
timeline: timeline,
|
|
1093
|
-
stats,
|
|
1094
|
-
flag: {
|
|
1095
|
-
overwritten: false,
|
|
1096
|
-
restarted: false
|
|
1097
|
-
}
|
|
1703
|
+
...config.context,
|
|
1704
|
+
policy: config.policy
|
|
1098
1705
|
};
|
|
1099
|
-
const
|
|
1100
|
-
const handleInterceptor = this._createInterceptorHandler(ctx, addTimeline);
|
|
1101
|
-
addTimeline("PROCESS_HANDLING", {
|
|
1102
|
-
type: "REQUEST_START",
|
|
1103
|
-
data: {}
|
|
1104
|
-
});
|
|
1706
|
+
const throwError = (err) => { throw err; };
|
|
1105
1707
|
try {
|
|
1106
|
-
|
|
1107
|
-
new
|
|
1108
|
-
}
|
|
1109
|
-
catch {
|
|
1110
|
-
throw new VigorFetchError("INVALID_PROTOCOL", {
|
|
1708
|
+
if (isVigorEmpty(ctx.policy.origin))
|
|
1709
|
+
throw new VigorFetchError("VALUE_REQUIRED", {
|
|
1111
1710
|
method: "request",
|
|
1112
|
-
data: {
|
|
1113
|
-
expected: ["valid URL protocol"],
|
|
1114
|
-
received: stats.origin
|
|
1115
|
-
}
|
|
1711
|
+
data: { key: "origin" }
|
|
1116
1712
|
});
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
const { headers, body, ...others } = stats.options;
|
|
1123
|
-
const hasBody = body !== VigorDefault &&
|
|
1124
|
-
body !== undefined;
|
|
1125
|
-
const method = stats.method || (hasBody ? 'POST' : 'GET');
|
|
1126
|
-
ctx.options = {
|
|
1127
|
-
...others,
|
|
1713
|
+
ctx.href = this._buildUrl(ctx.policy.origin, ctx.policy.path, ctx.policy.query, ctx.policy.hash);
|
|
1714
|
+
const hasBody = !isVigorEmpty(ctx.policy.body) && ctx.policy.body !== undefined;
|
|
1715
|
+
const method = ctx.policy.method || (hasBody ? "POST" : "GET");
|
|
1716
|
+
let options = {
|
|
1717
|
+
...ctx.policy.extra,
|
|
1128
1718
|
method: method,
|
|
1129
1719
|
headers: {}
|
|
1130
1720
|
};
|
|
1131
1721
|
if (hasBody) {
|
|
1132
|
-
const normalized = this.
|
|
1133
|
-
if (normalized.body !== undefined)
|
|
1134
|
-
|
|
1722
|
+
const normalized = this._normalizeBody(ctx.policy.body);
|
|
1723
|
+
if (normalized.body !== undefined)
|
|
1724
|
+
options.body = normalized.body;
|
|
1725
|
+
Object.assign(options.headers, normalized.headers);
|
|
1726
|
+
}
|
|
1727
|
+
Object.assign(options.headers, ctx.policy.headers);
|
|
1728
|
+
for (const middleware of ctx.policy.middlewares.before) {
|
|
1729
|
+
if (middleware._mode === "fluent") {
|
|
1730
|
+
await middleware.func(ctx);
|
|
1731
|
+
}
|
|
1732
|
+
else {
|
|
1733
|
+
ctx = await middleware.func(ctx, {
|
|
1734
|
+
throwError,
|
|
1735
|
+
setOptions: (o) => { options = o; },
|
|
1736
|
+
setHeaders: (h) => { options.headers = h; },
|
|
1737
|
+
setBody: (b) => { options.body = b; }
|
|
1738
|
+
});
|
|
1135
1739
|
}
|
|
1136
|
-
Object.assign(ctx.options.headers, normalized.headers);
|
|
1137
1740
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
};
|
|
1147
|
-
const throwStatus = async (ctx2, api) => {
|
|
1148
|
-
const response = ctx2.result;
|
|
1741
|
+
ctx.options = options;
|
|
1742
|
+
const retryEngine = new VigorRetry(ctx.policy.retry)
|
|
1743
|
+
.target(async (signal) => {
|
|
1744
|
+
return await fetch(ctx.href, { ...options, signal });
|
|
1745
|
+
})
|
|
1746
|
+
.middlewares(m => m
|
|
1747
|
+
.after("intercept", async (rctx, api) => {
|
|
1748
|
+
const response = rctx.result;
|
|
1149
1749
|
if (!response.ok) {
|
|
1750
|
+
let parsed = undefined;
|
|
1751
|
+
try {
|
|
1752
|
+
parsed = await new VigorParse(ctx.policy.parse).target(response.clone()).request();
|
|
1753
|
+
}
|
|
1754
|
+
catch {
|
|
1755
|
+
}
|
|
1150
1756
|
api.throwError(new VigorFetchError("FETCH_FAILED", {
|
|
1151
1757
|
method: "request",
|
|
1152
1758
|
data: {
|
|
1153
1759
|
status: response.status,
|
|
1154
|
-
|
|
1760
|
+
statusText: response.statusText,
|
|
1761
|
+
response,
|
|
1155
1762
|
url: response.url,
|
|
1156
|
-
|
|
1157
|
-
body: response.body,
|
|
1158
|
-
statusText: response.statusText
|
|
1763
|
+
parsed
|
|
1159
1764
|
}
|
|
1160
1765
|
}));
|
|
1161
1766
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
api.setDelay(delay + Math.random() * ctx2.stats.settings.jitter);
|
|
1196
|
-
}
|
|
1767
|
+
return rctx;
|
|
1768
|
+
})
|
|
1769
|
+
.retryIf("intercept", async (rctx, api) => {
|
|
1770
|
+
const response = rctx.error instanceof VigorFetchError
|
|
1771
|
+
? rctx.error.data?.response
|
|
1772
|
+
: undefined;
|
|
1773
|
+
if (response && ctx.policy.settings.unretryStatus.includes(response.status))
|
|
1774
|
+
api.cancelRetry();
|
|
1775
|
+
else
|
|
1776
|
+
api.proceedRetry();
|
|
1777
|
+
return rctx;
|
|
1778
|
+
})
|
|
1779
|
+
.onRetry("intercept", async (rctx, api) => {
|
|
1780
|
+
const response = rctx.error instanceof VigorFetchError
|
|
1781
|
+
? rctx.error.data?.response
|
|
1782
|
+
: undefined;
|
|
1783
|
+
if (response?.status === 429) {
|
|
1784
|
+
let retryHeader = null;
|
|
1785
|
+
for (const header of ctx.policy.settings.retryHeaders) {
|
|
1786
|
+
retryHeader = response.headers.get(header);
|
|
1787
|
+
if (retryHeader)
|
|
1788
|
+
break;
|
|
1789
|
+
}
|
|
1790
|
+
if (retryHeader) {
|
|
1791
|
+
const toNumber = Number(retryHeader);
|
|
1792
|
+
const delay = !isNaN(toNumber)
|
|
1793
|
+
? toNumber * 1000
|
|
1794
|
+
: (() => {
|
|
1795
|
+
const toDate = new Date(retryHeader).getTime();
|
|
1796
|
+
return !isNaN(toDate) ? toDate - Date.now() : null;
|
|
1797
|
+
})();
|
|
1798
|
+
if (delay !== null && delay > 0)
|
|
1799
|
+
api.setDelay(delay);
|
|
1197
1800
|
}
|
|
1198
1801
|
}
|
|
1199
|
-
|
|
1200
|
-
stats.retryConfig.interceptors.after = [throwStatus, ...stats.retryConfig.interceptors.after];
|
|
1201
|
-
stats.retryConfig.interceptors.retryIf = [handleBlacklist, ...stats.retryConfig.interceptors.retryIf];
|
|
1202
|
-
stats.retryConfig.interceptors.onRetry = [handleRatelimit, ...stats.retryConfig.interceptors.onRetry];
|
|
1203
|
-
const retryEngine = new VigorRetry(stats.retryConfig)
|
|
1204
|
-
.target(fetchTask);
|
|
1205
|
-
const parseEngine = new VigorParse(stats.parseConfig);
|
|
1206
|
-
addTimeline("ENGINE_CREATED", {
|
|
1207
|
-
retryEngine,
|
|
1208
|
-
parseEngine
|
|
1209
|
-
});
|
|
1210
|
-
await handleInterceptor("before", (interceptorType, func) => ({
|
|
1211
|
-
throwError: (error) => {
|
|
1212
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1213
|
-
interceptorType,
|
|
1214
|
-
interceptor: func,
|
|
1215
|
-
method: "throwError",
|
|
1216
|
-
args: [error]
|
|
1217
|
-
});
|
|
1218
|
-
throw error;
|
|
1219
|
-
},
|
|
1220
|
-
setOptions: (unknown) => {
|
|
1221
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1222
|
-
interceptorType,
|
|
1223
|
-
interceptor: func,
|
|
1224
|
-
method: "setOptions",
|
|
1225
|
-
args: [unknown]
|
|
1226
|
-
});
|
|
1227
|
-
return ctx.options = unknown;
|
|
1228
|
-
},
|
|
1229
|
-
setHeaders: (unknown) => {
|
|
1230
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1231
|
-
interceptorType,
|
|
1232
|
-
interceptor: func,
|
|
1233
|
-
method: "setHeaders",
|
|
1234
|
-
args: [unknown]
|
|
1235
|
-
});
|
|
1236
|
-
return ctx.options.headers = unknown;
|
|
1237
|
-
},
|
|
1238
|
-
setBody: (unknown) => {
|
|
1239
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1240
|
-
interceptorType,
|
|
1241
|
-
interceptor: func,
|
|
1242
|
-
method: "setBody",
|
|
1243
|
-
args: [unknown]
|
|
1244
|
-
});
|
|
1245
|
-
return ctx.options.body = unknown;
|
|
1246
|
-
}
|
|
1802
|
+
return rctx;
|
|
1247
1803
|
}));
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
const retryEnd = performance.now();
|
|
1255
|
-
addTimeline("RETRY_ENDED", {
|
|
1256
|
-
engine: retryEngine,
|
|
1257
|
-
timeline: retryTimeline,
|
|
1258
|
-
took: retryEnd - retryStart,
|
|
1259
|
-
response: ctx.response
|
|
1260
|
-
});
|
|
1261
|
-
addTimeline("PARSE_STARTED", {
|
|
1262
|
-
engine: parseEngine
|
|
1263
|
-
});
|
|
1264
|
-
const parseStart = performance.now();
|
|
1265
|
-
const parseTimeline = [];
|
|
1266
|
-
ctx.result = await parseEngine.target(ctx.response).request(undefined, parseTimeline);
|
|
1267
|
-
const parseEnd = performance.now();
|
|
1268
|
-
addTimeline("PARSE_ENDED", {
|
|
1269
|
-
engine: parseEngine,
|
|
1270
|
-
timeline: parseTimeline,
|
|
1271
|
-
took: parseEnd - parseStart,
|
|
1272
|
-
result: ctx.result
|
|
1273
|
-
});
|
|
1274
|
-
await handleInterceptor("after", (interceptorType, func) => ({
|
|
1275
|
-
setResult: (unknown) => {
|
|
1276
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1277
|
-
interceptorType,
|
|
1278
|
-
interceptor: func,
|
|
1279
|
-
method: "setResult",
|
|
1280
|
-
args: [unknown]
|
|
1281
|
-
});
|
|
1282
|
-
ctx.result = unknown;
|
|
1283
|
-
return unknown;
|
|
1284
|
-
},
|
|
1285
|
-
throwError: (error) => {
|
|
1286
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1287
|
-
interceptorType,
|
|
1288
|
-
interceptor: func,
|
|
1289
|
-
method: "throwError",
|
|
1290
|
-
args: [error]
|
|
1291
|
-
});
|
|
1292
|
-
throw error;
|
|
1293
|
-
},
|
|
1294
|
-
}));
|
|
1295
|
-
await handleInterceptor("result", (interceptorType, func) => ({
|
|
1296
|
-
setResult: (unknown) => {
|
|
1297
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1298
|
-
interceptorType,
|
|
1299
|
-
interceptor: func,
|
|
1300
|
-
method: "setResult",
|
|
1301
|
-
args: [unknown]
|
|
1302
|
-
});
|
|
1303
|
-
ctx.result = unknown;
|
|
1304
|
-
return unknown;
|
|
1305
|
-
},
|
|
1306
|
-
throwError: (error) => {
|
|
1307
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1308
|
-
interceptorType,
|
|
1309
|
-
interceptor: func,
|
|
1310
|
-
method: "throwError",
|
|
1311
|
-
args: [error]
|
|
1312
|
-
});
|
|
1313
|
-
throw error;
|
|
1314
|
-
},
|
|
1315
|
-
}));
|
|
1316
|
-
return ctx.result;
|
|
1317
|
-
}
|
|
1318
|
-
catch (error) {
|
|
1319
|
-
ctx.error = error;
|
|
1320
|
-
addTimeline("PROCESS_HANDLING", {
|
|
1321
|
-
type: "REQUEST_ERROR",
|
|
1322
|
-
data: {
|
|
1323
|
-
error
|
|
1804
|
+
ctx.response = await retryEngine.request();
|
|
1805
|
+
const parseEngine = new VigorParse(ctx.policy.parse).target(ctx.response);
|
|
1806
|
+
ctx.result = await parseEngine.request();
|
|
1807
|
+
for (const middleware of ctx.policy.middlewares.after) {
|
|
1808
|
+
if (middleware._mode === "fluent") {
|
|
1809
|
+
await middleware.func(ctx);
|
|
1324
1810
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
setResult: (unknown) => {
|
|
1328
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1329
|
-
interceptorType,
|
|
1330
|
-
interceptor: func,
|
|
1331
|
-
method: "setResult",
|
|
1332
|
-
args: [unknown]
|
|
1333
|
-
});
|
|
1334
|
-
ctx.result = unknown;
|
|
1335
|
-
ctx.flag.overwritten = true;
|
|
1336
|
-
return unknown;
|
|
1337
|
-
},
|
|
1338
|
-
throwError: (error) => {
|
|
1339
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1340
|
-
interceptorType,
|
|
1341
|
-
interceptor: func,
|
|
1342
|
-
method: "throwError",
|
|
1343
|
-
args: [error]
|
|
1344
|
-
});
|
|
1345
|
-
throw error;
|
|
1346
|
-
},
|
|
1347
|
-
restart: () => {
|
|
1348
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1349
|
-
interceptorType,
|
|
1350
|
-
interceptor: func,
|
|
1351
|
-
method: "restart",
|
|
1352
|
-
args: []
|
|
1353
|
-
});
|
|
1354
|
-
ctx.flag.restarted = true;
|
|
1811
|
+
else {
|
|
1812
|
+
ctx = await middleware.func(ctx, { throwError, setResult: (r) => { ctx.result = r; } });
|
|
1355
1813
|
}
|
|
1356
|
-
}));
|
|
1357
|
-
if (ctx.flag.restarted) {
|
|
1358
|
-
return await this.request(stats, ctx.timeline);
|
|
1359
|
-
}
|
|
1360
|
-
if (ctx.flag.overwritten)
|
|
1361
|
-
return ctx.result;
|
|
1362
|
-
if (stats.settings.default !== VigorDefault)
|
|
1363
|
-
return stats.settings.default;
|
|
1364
|
-
throw error;
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
class VigorAllSettings extends VigorStatus {
|
|
1369
|
-
constructor(config) {
|
|
1370
|
-
const base = {
|
|
1371
|
-
concurrency: 5,
|
|
1372
|
-
onlySuccess: false
|
|
1373
|
-
};
|
|
1374
|
-
super(config, base, (c) => new VigorAllSettings(c));
|
|
1375
|
-
}
|
|
1376
|
-
concurrency(num) { return this._next({ concurrency: num }); }
|
|
1377
|
-
onlySuccess(num) { return this._next({ onlySuccess: num }); }
|
|
1378
|
-
}
|
|
1379
|
-
class VigorAllInterceptors extends VigorStatus {
|
|
1380
|
-
constructor(config) {
|
|
1381
|
-
const base = {
|
|
1382
|
-
before: [],
|
|
1383
|
-
after: [],
|
|
1384
|
-
result: [],
|
|
1385
|
-
onError: []
|
|
1386
|
-
};
|
|
1387
|
-
super(config, base, (c) => new VigorAllInterceptors(c));
|
|
1388
|
-
}
|
|
1389
|
-
before(...funcs) { return this._next({ before: funcs.flat() }); }
|
|
1390
|
-
after(...funcs) { return this._next({ after: funcs.flat() }); }
|
|
1391
|
-
result(...funcs) { return this._next({ result: funcs.flat() }); }
|
|
1392
|
-
onError(...funcs) { return this._next({ onError: funcs.flat() }); }
|
|
1393
|
-
}
|
|
1394
|
-
class VigorAll extends VigorStatus {
|
|
1395
|
-
constructor(config) {
|
|
1396
|
-
const base = {
|
|
1397
|
-
target: [],
|
|
1398
|
-
settings: new VigorAllSettings()._getBase(),
|
|
1399
|
-
interceptors: new VigorAllInterceptors()._getBase()
|
|
1400
|
-
};
|
|
1401
|
-
super(config, base, (c) => new VigorAll(c));
|
|
1402
|
-
}
|
|
1403
|
-
_createTimelineHandler(timeline) {
|
|
1404
|
-
return (action, content) => {
|
|
1405
|
-
timeline.push({
|
|
1406
|
-
action: action,
|
|
1407
|
-
content: content,
|
|
1408
|
-
time: Date.now()
|
|
1409
|
-
});
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1412
|
-
_createInterceptorHandler(ctx, addTimeline) {
|
|
1413
|
-
return async (interceptorType, api) => {
|
|
1414
|
-
const interceptorsConfig = ctx["stats"]["interceptors"];
|
|
1415
|
-
const interceptors = interceptorsConfig[interceptorType];
|
|
1416
|
-
addTimeline("INTERCEPTOR_LOOP_STARTED", {
|
|
1417
|
-
interceptorType: interceptorType,
|
|
1418
|
-
interceptors,
|
|
1419
|
-
});
|
|
1420
|
-
const startTime = performance.now();
|
|
1421
|
-
for (const func of interceptors) {
|
|
1422
|
-
const scopedApi = api(interceptorType, func);
|
|
1423
|
-
await func(ctx, scopedApi);
|
|
1424
|
-
}
|
|
1425
|
-
const endTime = performance.now();
|
|
1426
|
-
addTimeline("INTERCEPTOR_LOOP_ENDED", {
|
|
1427
|
-
interceptorType: interceptorType,
|
|
1428
|
-
interceptors,
|
|
1429
|
-
took: endTime - startTime
|
|
1430
|
-
});
|
|
1431
|
-
};
|
|
1432
|
-
}
|
|
1433
|
-
_createEachTimelineHandler(timeline) {
|
|
1434
|
-
return (action, content) => {
|
|
1435
|
-
timeline.push({
|
|
1436
|
-
action: action,
|
|
1437
|
-
content: content,
|
|
1438
|
-
time: Date.now()
|
|
1439
|
-
});
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
_createEachInterceptorHandler(ctx, addEachTimeline) {
|
|
1443
|
-
return async (interceptorType, api) => {
|
|
1444
|
-
const interceptorsConfig = ctx["stats"]["interceptors"];
|
|
1445
|
-
const interceptors = interceptorsConfig[interceptorType];
|
|
1446
|
-
addEachTimeline("INTERCEPTOR_LOOP_STARTED", {
|
|
1447
|
-
interceptorType: interceptorType,
|
|
1448
|
-
interceptors,
|
|
1449
|
-
});
|
|
1450
|
-
const startTime = performance.now();
|
|
1451
|
-
for (const func of interceptors) {
|
|
1452
|
-
const scopedApi = api(interceptorType, func);
|
|
1453
|
-
await func(ctx, scopedApi);
|
|
1454
|
-
}
|
|
1455
|
-
const endTime = performance.now();
|
|
1456
|
-
addEachTimeline("INTERCEPTOR_LOOP_ENDED", {
|
|
1457
|
-
interceptorType: interceptorType,
|
|
1458
|
-
interceptors,
|
|
1459
|
-
took: endTime - startTime
|
|
1460
|
-
});
|
|
1461
|
-
};
|
|
1462
|
-
}
|
|
1463
|
-
target(...funcs) { return this._next({ target: funcs.flat() }); }
|
|
1464
|
-
settings(func) {
|
|
1465
|
-
if (func instanceof VigorAllSettings) {
|
|
1466
|
-
return this._next({ settings: func._getConfig() });
|
|
1467
|
-
}
|
|
1468
|
-
if (typeof func === 'function') {
|
|
1469
|
-
return this._next({ settings: func(new VigorAllSettings(this._config.settings))._getConfig() });
|
|
1470
|
-
}
|
|
1471
|
-
return this._next({ settings: func });
|
|
1472
|
-
}
|
|
1473
|
-
interceptors(func) {
|
|
1474
|
-
if (func instanceof VigorAllInterceptors) {
|
|
1475
|
-
return this._next({ interceptors: func._getConfig() });
|
|
1476
|
-
}
|
|
1477
|
-
if (typeof func === 'function') {
|
|
1478
|
-
return this._next({ interceptors: func(new VigorAllInterceptors(this._config.interceptors))._getConfig() });
|
|
1479
|
-
}
|
|
1480
|
-
return this._next({ interceptors: func });
|
|
1481
|
-
}
|
|
1482
|
-
async runTask(task, { stats, root }, semaphore) {
|
|
1483
|
-
let ctx = {
|
|
1484
|
-
result: VigorDefault,
|
|
1485
|
-
error: VigorDefault,
|
|
1486
|
-
timeline: [],
|
|
1487
|
-
stats,
|
|
1488
|
-
root,
|
|
1489
|
-
target: task,
|
|
1490
|
-
semaphore,
|
|
1491
|
-
flag: {
|
|
1492
|
-
overwritten: false
|
|
1493
|
-
}
|
|
1494
|
-
};
|
|
1495
|
-
const addEachTimeline = this._createEachTimelineHandler(ctx.timeline);
|
|
1496
|
-
const handleEachInterceptor = this._createEachInterceptorHandler(ctx, addEachTimeline);
|
|
1497
|
-
addEachTimeline("PROCESS_HANDLING", {
|
|
1498
|
-
type: "TASK_START",
|
|
1499
|
-
data: {}
|
|
1500
|
-
});
|
|
1501
|
-
try {
|
|
1502
|
-
try {
|
|
1503
|
-
await semaphore.acquire();
|
|
1504
|
-
addEachTimeline("TASK_ACQUIRED", {
|
|
1505
|
-
target: ctx.target
|
|
1506
|
-
});
|
|
1507
|
-
await handleEachInterceptor("before", (interceptorType, func) => ({
|
|
1508
|
-
throwError: (error) => {
|
|
1509
|
-
addEachTimeline("INTERCEPTOR_API_CALLED", {
|
|
1510
|
-
interceptorType,
|
|
1511
|
-
interceptor: func,
|
|
1512
|
-
method: "throwError",
|
|
1513
|
-
args: [error]
|
|
1514
|
-
});
|
|
1515
|
-
throw error;
|
|
1516
|
-
}
|
|
1517
|
-
}));
|
|
1518
|
-
addEachTimeline("TASK_STARTED", {
|
|
1519
|
-
target: ctx.target
|
|
1520
|
-
});
|
|
1521
|
-
const startTime = performance.now();
|
|
1522
|
-
ctx.result = await ctx.target(ctx);
|
|
1523
|
-
const endTime = performance.now();
|
|
1524
|
-
addEachTimeline("TASK_ENDED", {
|
|
1525
|
-
target: ctx.target,
|
|
1526
|
-
took: endTime - startTime
|
|
1527
|
-
});
|
|
1528
|
-
await handleEachInterceptor("after", (interceptorType, func) => ({
|
|
1529
|
-
setResult: (unknown) => {
|
|
1530
|
-
addEachTimeline("INTERCEPTOR_API_CALLED", {
|
|
1531
|
-
interceptorType,
|
|
1532
|
-
interceptor: func,
|
|
1533
|
-
method: "setResult",
|
|
1534
|
-
args: [unknown]
|
|
1535
|
-
});
|
|
1536
|
-
ctx.result = unknown;
|
|
1537
|
-
return unknown;
|
|
1538
|
-
},
|
|
1539
|
-
throwError: (error) => {
|
|
1540
|
-
addEachTimeline("INTERCEPTOR_API_CALLED", {
|
|
1541
|
-
interceptorType,
|
|
1542
|
-
interceptor: func,
|
|
1543
|
-
method: "throwError",
|
|
1544
|
-
args: [error]
|
|
1545
|
-
});
|
|
1546
|
-
throw error;
|
|
1547
|
-
}
|
|
1548
|
-
}));
|
|
1549
1814
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
}
|
|
1815
|
+
const setResult = (r) => { ctx.result = r; };
|
|
1816
|
+
for (const middleware of ctx.policy.middlewares.onResult) {
|
|
1817
|
+
if (middleware._mode === "fluent") {
|
|
1818
|
+
ctx.result = await middleware.func(ctx.result);
|
|
1819
|
+
}
|
|
1820
|
+
else {
|
|
1821
|
+
ctx = await middleware.func(ctx, { setResult, throwError });
|
|
1822
|
+
}
|
|
1555
1823
|
}
|
|
1824
|
+
return {
|
|
1825
|
+
success: true,
|
|
1826
|
+
data: ctx.result,
|
|
1827
|
+
error: null
|
|
1828
|
+
};
|
|
1556
1829
|
}
|
|
1557
1830
|
catch (error) {
|
|
1558
1831
|
ctx.error = error;
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1832
|
+
ctx.flags.overrided = false;
|
|
1833
|
+
ctx.flags.restarted = false;
|
|
1834
|
+
const setResult = (r) => {
|
|
1835
|
+
ctx.flags.overrided = true;
|
|
1836
|
+
ctx.result = r;
|
|
1837
|
+
};
|
|
1838
|
+
const proceedRestart = () => ctx.flags.restarted = true;
|
|
1839
|
+
const cancelRestart = () => ctx.flags.restarted = false;
|
|
1840
|
+
for (const middleware of ctx.policy.middlewares.onError) {
|
|
1841
|
+
if (middleware._mode === "fluent") {
|
|
1842
|
+
await middleware.func(ctx.error);
|
|
1563
1843
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
return unknown;
|
|
1576
|
-
},
|
|
1577
|
-
throwError: (error) => {
|
|
1578
|
-
addEachTimeline("INTERCEPTOR_API_CALLED", {
|
|
1579
|
-
interceptorType,
|
|
1580
|
-
interceptor: func,
|
|
1581
|
-
method: "throwError",
|
|
1582
|
-
args: [error]
|
|
1844
|
+
else {
|
|
1845
|
+
ctx = await middleware.func(ctx, { setResult, throwError, proceedRestart, cancelRestart });
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
if (ctx.flags.restarted) {
|
|
1849
|
+
if (restartAttempt + 1 > ctx.policy.settings.maxRestarts)
|
|
1850
|
+
throw new VigorFetchError("RESTART_EXHAUSTED", {
|
|
1851
|
+
method: "request",
|
|
1852
|
+
data: {
|
|
1853
|
+
maxAttempts: ctx.policy.settings.maxRestarts
|
|
1854
|
+
}
|
|
1583
1855
|
});
|
|
1584
|
-
|
|
1585
|
-
},
|
|
1586
|
-
}));
|
|
1587
|
-
if (ctx.flag.overwritten)
|
|
1588
|
-
return ctx.result;
|
|
1589
|
-
throw error;
|
|
1590
|
-
}
|
|
1591
|
-
return ctx.result;
|
|
1592
|
-
}
|
|
1593
|
-
async request(config, timeline = []) {
|
|
1594
|
-
const stats = this._mergeConfig(this._config, config);
|
|
1595
|
-
let ctx = {
|
|
1596
|
-
result: VigorDefault,
|
|
1597
|
-
timeline,
|
|
1598
|
-
stats,
|
|
1599
|
-
queue: new Set(),
|
|
1600
|
-
active: 0
|
|
1601
|
-
};
|
|
1602
|
-
const addTimeline = this._createTimelineHandler(ctx.timeline);
|
|
1603
|
-
const handleInterceptor = this._createInterceptorHandler(ctx, addTimeline);
|
|
1604
|
-
addTimeline("PROCESS_HANDLING", {
|
|
1605
|
-
type: "REQUEST_START",
|
|
1606
|
-
data: {}
|
|
1607
|
-
});
|
|
1608
|
-
if (stats.target.length === 0)
|
|
1609
|
-
throw new VigorAllError("EMPTY_TARGET", {
|
|
1610
|
-
method: "request",
|
|
1611
|
-
data: {}
|
|
1612
|
-
});
|
|
1613
|
-
const waitQueue = [];
|
|
1614
|
-
const acquire = () => {
|
|
1615
|
-
if (ctx.active < stats.settings.concurrency) {
|
|
1616
|
-
ctx.active++;
|
|
1617
|
-
return Promise.resolve();
|
|
1856
|
+
return await this.requestRuntime(restartAttempt + 1);
|
|
1618
1857
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1858
|
+
if (ctx.flags.overrided)
|
|
1859
|
+
return {
|
|
1860
|
+
success: true,
|
|
1861
|
+
data: ctx.result,
|
|
1862
|
+
error: null
|
|
1863
|
+
};
|
|
1864
|
+
if (!isVigorEmpty(ctx.policy.settings.default)) {
|
|
1865
|
+
const data = await ctx.policy.settings.default(ctx);
|
|
1866
|
+
return {
|
|
1867
|
+
success: true,
|
|
1868
|
+
data,
|
|
1869
|
+
error: null
|
|
1870
|
+
};
|
|
1627
1871
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
.catch(err => ({ success: false, value: err }))
|
|
1634
|
-
.finally(() => ctx.queue.delete(promise));
|
|
1635
|
-
ctx.queue.add(promise);
|
|
1872
|
+
return {
|
|
1873
|
+
success: false,
|
|
1874
|
+
data: null,
|
|
1875
|
+
error
|
|
1876
|
+
};
|
|
1636
1877
|
}
|
|
1637
|
-
addTimeline("QUEUE_REQUEST_STARTED", {
|
|
1638
|
-
queue: ctx.queue
|
|
1639
|
-
});
|
|
1640
|
-
const startTime = performance.now();
|
|
1641
|
-
const raw = await Promise.all(ctx.queue);
|
|
1642
|
-
const endTime = performance.now();
|
|
1643
|
-
addTimeline("QUEUE_REQUEST_ENDED", {
|
|
1644
|
-
queue: ctx.queue,
|
|
1645
|
-
took: endTime - startTime
|
|
1646
|
-
});
|
|
1647
|
-
ctx.result = stats.settings.onlySuccess
|
|
1648
|
-
? raw.filter(r => r.success).map(r => r.value)
|
|
1649
|
-
: raw.map(r => r.value);
|
|
1650
|
-
await handleInterceptor("result", (interceptorType, func) => ({
|
|
1651
|
-
setResult: (unknown) => {
|
|
1652
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1653
|
-
interceptorType,
|
|
1654
|
-
interceptor: func,
|
|
1655
|
-
method: "setResult",
|
|
1656
|
-
args: [unknown]
|
|
1657
|
-
});
|
|
1658
|
-
ctx.result = unknown;
|
|
1659
|
-
return unknown;
|
|
1660
|
-
},
|
|
1661
|
-
throwError: (error) => {
|
|
1662
|
-
addTimeline("INTERCEPTOR_API_CALLED", {
|
|
1663
|
-
interceptorType,
|
|
1664
|
-
interceptor: func,
|
|
1665
|
-
method: "throwError",
|
|
1666
|
-
args: [error]
|
|
1667
|
-
});
|
|
1668
|
-
throw error;
|
|
1669
|
-
},
|
|
1670
|
-
}));
|
|
1671
|
-
return ctx.result;
|
|
1672
1878
|
}
|
|
1673
1879
|
}
|
|
1674
|
-
|
|
1675
|
-
retry: {
|
|
1676
|
-
main: VigorRetry,
|
|
1677
|
-
settings: VigorRetrySettings,
|
|
1678
|
-
interceptors: VigorRetryInterceptors,
|
|
1679
|
-
error: VigorRetryError,
|
|
1680
|
-
algorithms: {
|
|
1681
|
-
constant: VigorRetryAlgorithmsConstant,
|
|
1682
|
-
linear: VigorRetryAlgorithmsLinear,
|
|
1683
|
-
backoff: VigorRetryAlgorithmsBackoff,
|
|
1684
|
-
custom: VigorRetryAlgorithmsCustom
|
|
1685
|
-
}
|
|
1686
|
-
},
|
|
1687
|
-
parse: {
|
|
1688
|
-
main: VigorParse,
|
|
1689
|
-
settings: VigorParseSettings,
|
|
1690
|
-
interceptors: VigorParseInterceptors,
|
|
1691
|
-
error: VigorParseError,
|
|
1692
|
-
strategies: VigorParseStrategies
|
|
1693
|
-
},
|
|
1694
|
-
fetch: {
|
|
1695
|
-
main: VigorFetch,
|
|
1696
|
-
settings: VigorFetchSettings,
|
|
1697
|
-
interceptors: VigorFetchInterceptors,
|
|
1698
|
-
error: VigorFetchError,
|
|
1699
|
-
},
|
|
1700
|
-
all: {
|
|
1701
|
-
main: VigorAll,
|
|
1702
|
-
settings: VigorAllSettings,
|
|
1703
|
-
interceptors: VigorAllInterceptors,
|
|
1704
|
-
error: VigorAllError
|
|
1705
|
-
}
|
|
1706
|
-
};
|
|
1880
|
+
|
|
1707
1881
|
const vigor = {
|
|
1708
|
-
use:
|
|
1709
|
-
|
|
1710
|
-
},
|
|
1711
|
-
fetch: (str) => {
|
|
1712
|
-
return new VigorFetch().origin(str);
|
|
1713
|
-
},
|
|
1714
|
-
retry: (target) => {
|
|
1715
|
-
return new VigorRetry().target(target);
|
|
1882
|
+
use: (plugin, options) => {
|
|
1883
|
+
plugin.func(vigor, options);
|
|
1716
1884
|
},
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
all: (...
|
|
1721
|
-
return new VigorAll().target(...funcs);
|
|
1722
|
-
},
|
|
1723
|
-
builder: {
|
|
1724
|
-
fetch: {
|
|
1725
|
-
settings: (c) => new VigorFetchSettings(c),
|
|
1726
|
-
interceptors: (c) => new VigorFetchInterceptors(c),
|
|
1727
|
-
},
|
|
1728
|
-
retry: {
|
|
1729
|
-
settings: (c) => new VigorRetrySettings(c),
|
|
1730
|
-
interceptors: (c) => new VigorRetryInterceptors(c),
|
|
1731
|
-
},
|
|
1732
|
-
parse: {
|
|
1733
|
-
settings: (c) => new VigorParseSettings(c),
|
|
1734
|
-
interceptors: (c) => new VigorParseInterceptors(c),
|
|
1735
|
-
},
|
|
1736
|
-
all: {
|
|
1737
|
-
settings: (c) => new VigorAllSettings(c),
|
|
1738
|
-
interceptors: (c) => new VigorAllInterceptors(c),
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1885
|
+
retry: (task) => task ? new VigorRetry().target(task) : new VigorRetry(),
|
|
1886
|
+
parse: (response) => response ? new VigorParse().target(response) : new VigorParse(),
|
|
1887
|
+
fetch: (origin) => origin ? new VigorFetch().origin(origin) : new VigorFetch(),
|
|
1888
|
+
all: (...tasks) => tasks.length > 0 ? new VigorAll().target(...tasks) : new VigorAll()
|
|
1741
1889
|
};
|
|
1742
1890
|
|
|
1891
|
+
exports.VigorAll = VigorAll;
|
|
1892
|
+
exports.VigorAllBase = VigorAllBase;
|
|
1743
1893
|
exports.VigorAllError = VigorAllError;
|
|
1744
|
-
exports.
|
|
1894
|
+
exports.VigorAllErrorMessageFuncs = VigorAllErrorMessageFuncs;
|
|
1895
|
+
exports.VigorAllPolicyBase = VigorAllPolicyBase;
|
|
1896
|
+
exports.VigorAllPolicyMiddlewares = VigorAllPolicyMiddlewares;
|
|
1897
|
+
exports.VigorAllPolicyMiddlewaresBase = VigorAllPolicyMiddlewaresBase;
|
|
1898
|
+
exports.VigorAllPolicySettings = VigorAllPolicySettings;
|
|
1899
|
+
exports.VigorAllPolicySettingsBase = VigorAllPolicySettingsBase;
|
|
1900
|
+
exports.VigorError = VigorError;
|
|
1901
|
+
exports.VigorFetch = VigorFetch;
|
|
1902
|
+
exports.VigorFetchBase = VigorFetchBase;
|
|
1745
1903
|
exports.VigorFetchError = VigorFetchError;
|
|
1904
|
+
exports.VigorFetchErrorMessageFuncs = VigorFetchErrorMessageFuncs;
|
|
1905
|
+
exports.VigorFetchPolicyBase = VigorFetchPolicyBase;
|
|
1906
|
+
exports.VigorFetchPolicyMiddlewares = VigorFetchPolicyMiddlewares;
|
|
1907
|
+
exports.VigorFetchPolicyMiddlewaresBase = VigorFetchPolicyMiddlewaresBase;
|
|
1908
|
+
exports.VigorFetchPolicySettings = VigorFetchPolicySettings;
|
|
1909
|
+
exports.VigorFetchPolicySettingsBase = VigorFetchPolicySettingsBase;
|
|
1910
|
+
exports.VigorJitter = VigorJitter;
|
|
1911
|
+
exports.VigorParse = VigorParse;
|
|
1912
|
+
exports.VigorParseBase = VigorParseBase;
|
|
1913
|
+
exports.VigorParseContentTypeStrategy = VigorParseContentTypeStrategy;
|
|
1746
1914
|
exports.VigorParseError = VigorParseError;
|
|
1915
|
+
exports.VigorParseErrorMessageFuncs = VigorParseErrorMessageFuncs;
|
|
1916
|
+
exports.VigorParsePolicyBase = VigorParsePolicyBase;
|
|
1917
|
+
exports.VigorParsePolicyMiddlewares = VigorParsePolicyMiddlewares;
|
|
1918
|
+
exports.VigorParsePolicyMiddlewaresBase = VigorParsePolicyMiddlewaresBase;
|
|
1919
|
+
exports.VigorParsePolicySettings = VigorParsePolicySettings;
|
|
1920
|
+
exports.VigorParsePolicySettingsBase = VigorParsePolicySettingsBase;
|
|
1921
|
+
exports.VigorParsePolicyStrategies = VigorParsePolicyStrategies;
|
|
1922
|
+
exports.VigorParsePolicyStrategiesBase = VigorParsePolicyStrategiesBase;
|
|
1923
|
+
exports.VigorParseSniffStrategy = VigorParseSniffStrategy;
|
|
1924
|
+
exports.VigorRetry = VigorRetry;
|
|
1925
|
+
exports.VigorRetryBase = VigorRetryBase;
|
|
1747
1926
|
exports.VigorRetryError = VigorRetryError;
|
|
1927
|
+
exports.VigorRetryErrorMessageFuncs = VigorRetryErrorMessageFuncs;
|
|
1928
|
+
exports.VigorRetryPolicyAlgorithms = VigorRetryPolicyAlgorithms;
|
|
1929
|
+
exports.VigorRetryPolicyAlgorithmsBackoff = VigorRetryPolicyAlgorithmsBackoff;
|
|
1930
|
+
exports.VigorRetryPolicyAlgorithmsBackoffBase = VigorRetryPolicyAlgorithmsBackoffBase;
|
|
1931
|
+
exports.VigorRetryPolicyAlgorithmsBase = VigorRetryPolicyAlgorithmsBase;
|
|
1932
|
+
exports.VigorRetryPolicyAlgorithmsConstant = VigorRetryPolicyAlgorithmsConstant;
|
|
1933
|
+
exports.VigorRetryPolicyAlgorithmsConstantBase = VigorRetryPolicyAlgorithmsConstantBase;
|
|
1934
|
+
exports.VigorRetryPolicyAlgorithmsCustom = VigorRetryPolicyAlgorithmsCustom;
|
|
1935
|
+
exports.VigorRetryPolicyAlgorithmsCustomBase = VigorRetryPolicyAlgorithmsCustomBase;
|
|
1936
|
+
exports.VigorRetryPolicyAlgorithmsLinear = VigorRetryPolicyAlgorithmsLinear;
|
|
1937
|
+
exports.VigorRetryPolicyAlgorithmsLinearBase = VigorRetryPolicyAlgorithmsLinearBase;
|
|
1938
|
+
exports.VigorRetryPolicyBase = VigorRetryPolicyBase;
|
|
1939
|
+
exports.VigorRetryPolicyMiddlewares = VigorRetryPolicyMiddlewares;
|
|
1940
|
+
exports.VigorRetryPolicyMiddlewaresBase = VigorRetryPolicyMiddlewaresBase;
|
|
1941
|
+
exports.VigorRetryPolicySettings = VigorRetryPolicySettings;
|
|
1942
|
+
exports.VigorRetryPolicySettingsBase = VigorRetryPolicySettingsBase;
|
|
1943
|
+
exports.VigorStatus = VigorStatus;
|
|
1748
1944
|
exports.default = vigor;
|
|
1749
1945
|
exports.vigor = vigor;
|