@gcmdev/faxios 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/adapters/fetch-helpers.d.ts +1 -1
- package/dist/lib/adapters/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/adapters/fetch.d.ts.map +1 -1
- package/dist/lib/adapters/fetch.js +86 -26
- package/dist/lib/adapters/fetch.js.map +1 -1
- package/dist/lib/cancel/CanceledError.d.ts +1 -1
- package/dist/lib/cancel/CanceledError.d.ts.map +1 -1
- package/dist/lib/core/Faxios.d.ts +3 -3
- package/dist/lib/core/Faxios.d.ts.map +1 -1
- package/dist/lib/core/Faxios.js +8 -4
- package/dist/lib/core/Faxios.js.map +1 -1
- package/dist/lib/core/FaxiosError.d.ts +2 -2
- package/dist/lib/core/FaxiosError.d.ts.map +1 -1
- package/dist/lib/core/FaxiosHeaders.d.ts +8 -8
- package/dist/lib/core/FaxiosHeaders.d.ts.map +1 -1
- package/dist/lib/core/define.d.ts +3 -3
- package/dist/lib/core/define.d.ts.map +1 -1
- package/dist/lib/core/dispatchRequest.d.ts +1 -1
- package/dist/lib/core/dispatchRequest.d.ts.map +1 -1
- package/dist/lib/core/mergeConfig.d.ts +2 -2
- package/dist/lib/core/mergeConfig.d.ts.map +1 -1
- package/dist/lib/core/mergeConfig.js +40 -2
- package/dist/lib/core/mergeConfig.js.map +1 -1
- package/dist/lib/core/route.d.ts +1 -1
- package/dist/lib/core/route.d.ts.map +1 -1
- package/dist/lib/core/route.js +2 -5
- package/dist/lib/core/route.js.map +1 -1
- package/dist/lib/core/transformData.d.ts +2 -2
- package/dist/lib/core/transformData.d.ts.map +1 -1
- package/dist/lib/core/validateSchema.d.ts +2 -2
- package/dist/lib/core/validateSchema.d.ts.map +1 -1
- package/dist/lib/env/data.d.ts +1 -1
- package/dist/lib/env/data.js +1 -1
- package/dist/lib/faxios.d.ts +50 -50
- package/dist/lib/faxios.d.ts.map +1 -1
- package/dist/lib/helpers/cloneConfig.d.ts +0 -1
- package/dist/lib/helpers/cloneConfig.d.ts.map +1 -1
- package/dist/lib/helpers/cloneConfig.js +2 -2
- package/dist/lib/helpers/cloneConfig.js.map +1 -1
- package/dist/lib/helpers/cookies.d.ts +1 -1
- package/dist/lib/helpers/cookies.d.ts.map +1 -1
- package/dist/lib/helpers/formDataToJSON.d.ts +1 -1
- package/dist/lib/helpers/formDataToJSON.d.ts.map +1 -1
- package/dist/lib/helpers/formDataToJSON.js +1 -1
- package/dist/lib/helpers/formDataToJSON.js.map +1 -1
- package/dist/lib/helpers/parseHeaders.d.ts +1 -1
- package/dist/lib/helpers/parseHeaders.d.ts.map +1 -1
- package/dist/lib/helpers/substitutePathParams.d.ts.map +1 -1
- package/dist/lib/helpers/substitutePathParams.js +11 -3
- package/dist/lib/helpers/substitutePathParams.js.map +1 -1
- package/dist/lib/helpers/toFormData.d.ts.map +1 -1
- package/dist/lib/helpers/toFormData.js +4 -2
- package/dist/lib/helpers/toFormData.js.map +1 -1
- package/dist/lib/helpers/validator.d.ts +1 -1
- package/dist/lib/helpers/validator.d.ts.map +1 -1
- package/dist/lib/platform.d.ts.map +1 -1
- package/dist/lib/plugins/authBearer.d.ts +9 -5
- package/dist/lib/plugins/authBearer.d.ts.map +1 -1
- package/dist/lib/plugins/authBearer.js +6 -0
- package/dist/lib/plugins/authBearer.js.map +1 -1
- package/dist/lib/plugins/definePlugin.d.ts +3 -3
- package/dist/lib/plugins/definePlugin.d.ts.map +1 -1
- package/dist/lib/plugins/retry.d.ts +23 -23
- package/dist/lib/plugins/retry.d.ts.map +1 -1
- package/dist/lib/plugins/timing.d.ts +8 -8
- package/dist/lib/plugins/timing.d.ts.map +1 -1
- package/dist/lib/types/standard-schema.d.ts +6 -6
- package/dist/lib/types/standard-schema.d.ts.map +1 -1
- package/dist/lib/types.d.ts +92 -92
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +4 -3
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +9 -2
- package/dist/lib/utils.js.map +1 -1
- package/package.json +7 -4
- package/skills/configure-requests/SKILL.md +8 -5
- package/skills/configure-requests/references/plugins.md +37 -6
- package/src/index.ts +15 -15
- package/src/lib/adapters/fetch-helpers.ts +3 -3
- package/src/lib/adapters/fetch.ts +125 -64
- package/src/lib/cancel/CanceledError.ts +1 -1
- package/src/lib/core/Faxios.ts +17 -13
- package/src/lib/core/FaxiosError.ts +3 -3
- package/src/lib/core/FaxiosHeaders.ts +16 -16
- package/src/lib/core/define.ts +7 -7
- package/src/lib/core/dispatchRequest.ts +2 -2
- package/src/lib/core/mergeConfig.ts +42 -4
- package/src/lib/core/prepareRequest.ts +1 -1
- package/src/lib/core/route.ts +3 -6
- package/src/lib/core/transformData.ts +2 -2
- package/src/lib/core/validateSchema.ts +4 -4
- package/src/lib/defaults/index.ts +1 -1
- package/src/lib/env/data.ts +1 -1
- package/src/lib/faxios.ts +55 -55
- package/src/lib/helpers/HttpStatusCode.ts +1 -1
- package/src/lib/helpers/cloneConfig.ts +2 -3
- package/src/lib/helpers/composeSignals.ts +4 -4
- package/src/lib/helpers/cookies.ts +3 -3
- package/src/lib/helpers/formDataToJSON.ts +4 -4
- package/src/lib/helpers/parseHeaders.ts +2 -2
- package/src/lib/helpers/progressEventReducer.ts +1 -1
- package/src/lib/helpers/sanitizeHeaderValue.ts +1 -1
- package/src/lib/helpers/substitutePathParams.ts +13 -3
- package/src/lib/helpers/throttle.ts +1 -1
- package/src/lib/helpers/toFormData.ts +11 -9
- package/src/lib/helpers/trackStream.ts +3 -3
- package/src/lib/helpers/validator.ts +3 -3
- package/src/lib/platform.ts +4 -4
- package/src/lib/plugins/authBearer.ts +14 -9
- package/src/lib/plugins/definePlugin.ts +3 -3
- package/src/lib/plugins/retry.ts +27 -27
- package/src/lib/plugins/timing.ts +5 -5
- package/src/lib/types/standard-schema.ts +6 -6
- package/src/lib/types.ts +125 -125
- package/src/lib/utils.ts +21 -12
|
@@ -11,7 +11,7 @@ export const progressEventReducer = (listener: (data: FaxiosProgressEvent) => vo
|
|
|
11
11
|
if (!e || typeof (e as Record<string, unknown>).loaded !== "number") {
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
-
const ev = e as { loaded: number; total?: number;
|
|
14
|
+
const ev = e as { lengthComputable?: boolean; loaded: number; total?: number; };
|
|
15
15
|
const rawLoaded = ev.loaded;
|
|
16
16
|
const total = ev.lengthComputable ? ev.total : undefined;
|
|
17
17
|
const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded;
|
|
@@ -36,7 +36,7 @@ const INVALID_UNICODE_HEADER_VALUE_CHARS = new RegExp("[\\u0000-\\u0008\\u000a-\
|
|
|
36
36
|
const INVALID_BYTE_STRING_HEADER_VALUE_CHARS = new RegExp("[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+", "g");
|
|
37
37
|
|
|
38
38
|
// eslint-disable-next-line sonarjs/function-return-type
|
|
39
|
-
function sanitizeValue(value: unknown, invalidChars: RegExp): string |
|
|
39
|
+
function sanitizeValue(value: unknown, invalidChars: RegExp): Array<string> | string {
|
|
40
40
|
if (utils.isArray(value)) {
|
|
41
41
|
return (value as Array<unknown>).map(item => sanitizeValue(item, invalidChars) as string);
|
|
42
42
|
}
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
+
import FaxiosError from "../core/FaxiosError.js";
|
|
2
|
+
|
|
3
|
+
// `.` and `..` (raw or percent-encoded) would become a dot segment that the URL parser resolves,
|
|
4
|
+
// moving the request to another path.
|
|
5
|
+
const DOT_SEGMENT = /^(?:\.|%2e){1,2}$/i;
|
|
6
|
+
|
|
1
7
|
/** Expects raw (unencoded) param values — applies encodeURIComponent here. */
|
|
2
8
|
export function substitutePathParams(url: string, params: Record<string, unknown>): string {
|
|
3
9
|
return url.replace(/\{([^{}]+)\}/g, (_match, key: string) => {
|
|
4
10
|
if (!Object.prototype.hasOwnProperty.call(params, key)) {
|
|
5
|
-
throw new
|
|
11
|
+
throw new FaxiosError(`Path param "${key}" not found in pathParams`, FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
6
12
|
}
|
|
7
13
|
const val = params[key];
|
|
8
14
|
if (val == null) {
|
|
9
|
-
throw new
|
|
15
|
+
throw new FaxiosError(`Path param "${key}" is null or undefined`, FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
10
16
|
}
|
|
11
|
-
|
|
17
|
+
const str = String(val);
|
|
18
|
+
if (DOT_SEGMENT.test(str)) {
|
|
19
|
+
throw new FaxiosError(`Path param "${key}" must not be "." or ".." (or a percent-encoded form)`, FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
20
|
+
}
|
|
21
|
+
return encodeURIComponent(str);
|
|
12
22
|
});
|
|
13
23
|
}
|
|
@@ -8,7 +8,7 @@ function throttle(fn: (...args: Array<unknown>) => void, freq: number): [(...arg
|
|
|
8
8
|
let timestamp = 0;
|
|
9
9
|
const threshold = 1000 / freq;
|
|
10
10
|
let lastArgs: Array<unknown> | null = null;
|
|
11
|
-
let timer: ReturnType<typeof setTimeout>
|
|
11
|
+
let timer: null | ReturnType<typeof setTimeout> = null;
|
|
12
12
|
|
|
13
13
|
const invoke = (args: Array<unknown>, now: number = Date.now()) => {
|
|
14
14
|
timestamp = now;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import FaxiosError from "../core/FaxiosError.js";
|
|
4
4
|
// temporary hotfix to avoid circular references until FaxiosURLSearchParams is refactored
|
|
5
|
-
import type { GenericFormData, SerializerVisitor
|
|
5
|
+
import type { FormDataVisitorHelpers, GenericFormData, SerializerVisitor } from "../types.js";
|
|
6
6
|
import utils from "../utils.js";
|
|
7
7
|
|
|
8
8
|
// Default nesting limit shared with the inverse transform (formDataToJSON) so
|
|
@@ -40,11 +40,11 @@ function removeBrackets(key: string): string {
|
|
|
40
40
|
*
|
|
41
41
|
* @returns {string} The path to the current key.
|
|
42
42
|
*/
|
|
43
|
-
function renderKey(path: Array<
|
|
43
|
+
function renderKey(path: Array<number | string> | null | undefined, key: number | string, dots: unknown): string {
|
|
44
44
|
if (!path) return String(key);
|
|
45
45
|
return path
|
|
46
46
|
.concat(key)
|
|
47
|
-
.map(function each(token:
|
|
47
|
+
.map(function each(token: number | string, i: number) {
|
|
48
48
|
token = removeBrackets(String(token));
|
|
49
49
|
return !dots && i ? "[" + token + "]" : token;
|
|
50
50
|
})
|
|
@@ -100,13 +100,15 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
|
|
|
100
100
|
formData = new FormDataCtor();
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
// Null-prototype destination: `visitor`, `maxDepth` and `Blob` are read below, and a polluted
|
|
104
|
+
// Object.prototype must not supply them.
|
|
103
105
|
options = utils.toFlatObject(
|
|
104
106
|
options,
|
|
105
|
-
{
|
|
107
|
+
Object.assign(Object.create(null) as Record<string, unknown>, {
|
|
106
108
|
metaTokens: true,
|
|
107
109
|
dots: false,
|
|
108
110
|
indexes: false,
|
|
109
|
-
},
|
|
111
|
+
}),
|
|
110
112
|
false,
|
|
111
113
|
function defined(option: string, source: unknown) {
|
|
112
114
|
return !utils.isUndefined((source as Record<string, unknown>)[option]);
|
|
@@ -201,7 +203,7 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
|
|
|
201
203
|
*
|
|
202
204
|
* @returns {boolean} return true to visit the each prop of the value recursively
|
|
203
205
|
*/
|
|
204
|
-
function defaultVisitor(this: GenericFormData, value: unknown, key:
|
|
206
|
+
function defaultVisitor(this: GenericFormData, value: unknown, key: number | string, path: Array<number | string> | null, _helpers?: FormDataVisitorHelpers): boolean {
|
|
205
207
|
let arr: unknown = value;
|
|
206
208
|
|
|
207
209
|
if (utils.isReactNative(formData) && utils.isReactNativeBlob(value)) {
|
|
@@ -254,7 +256,7 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
|
|
|
254
256
|
isVisitable,
|
|
255
257
|
});
|
|
256
258
|
|
|
257
|
-
function build(value: unknown, path?: Array<
|
|
259
|
+
function build(value: unknown, path?: Array<number | string>, depth = 0): void {
|
|
258
260
|
if (utils.isUndefined(value)) return;
|
|
259
261
|
|
|
260
262
|
throwIfMaxDepthExceeded(depth);
|
|
@@ -271,13 +273,13 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
|
|
|
271
273
|
visitor.call(
|
|
272
274
|
formData as GenericFormData,
|
|
273
275
|
el,
|
|
274
|
-
utils.isString(key) ? (key as string).trim() : key as
|
|
276
|
+
utils.isString(key) ? (key as string).trim() : key as number | string,
|
|
275
277
|
path ?? null,
|
|
276
278
|
exposedHelpers
|
|
277
279
|
);
|
|
278
280
|
|
|
279
281
|
if (result === true) {
|
|
280
|
-
build(el, path ? path.concat(key as
|
|
282
|
+
build(el, path ? path.concat(key as number | string) : [ key as number | string ], depth + 1);
|
|
281
283
|
}
|
|
282
284
|
});
|
|
283
285
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
type ReadableStreamController = {
|
|
2
|
-
enqueue: (chunk: Uint8Array) => void;
|
|
3
2
|
close: () => void;
|
|
3
|
+
enqueue: (chunk: Uint8Array) => void;
|
|
4
4
|
};
|
|
5
5
|
|
|
6
6
|
type ReadableStreamSource = {
|
|
7
|
-
pull?: (controller: ReadableStreamController) => Promise<void>;
|
|
8
7
|
cancel?: (reason?: unknown) => Promise<unknown>;
|
|
8
|
+
pull?: (controller: ReadableStreamController) => Promise<void>;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
type ReadableStreamInit = {
|
|
@@ -43,8 +43,8 @@ const readBytes = async function* (iterable: AsyncIterable<ByteChunk> | unknown,
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
type ReaderLike = {
|
|
46
|
-
read: () => Promise<{ done: boolean; value: ByteChunk; }>;
|
|
47
46
|
cancel: () => Promise<void>;
|
|
47
|
+
read: () => Promise<{ done: boolean; value: ByteChunk; }>;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
type StreamLike = {
|
|
@@ -5,7 +5,7 @@ import { VERSION } from "../env/data.js";
|
|
|
5
5
|
|
|
6
6
|
export type ValidatorFn = (value: unknown, opt?: string, opts?: unknown) => boolean | string;
|
|
7
7
|
|
|
8
|
-
const validators: Record<string,
|
|
8
|
+
const validators: Record<string, undefined | ValidatorFn> = {};
|
|
9
9
|
|
|
10
10
|
[ "object", "boolean", "number", "function", "string", "symbol" ].forEach((type, i) => {
|
|
11
11
|
validators[type] = function validator(thing: unknown): boolean | string {
|
|
@@ -24,7 +24,7 @@ const deprecatedWarnings: Record<string, boolean | undefined> = {};
|
|
|
24
24
|
*
|
|
25
25
|
* @returns {function}
|
|
26
26
|
*/
|
|
27
|
-
(validators as Record<string, unknown>)["transitional"] = function transitional(validator:
|
|
27
|
+
(validators as Record<string, unknown>)["transitional"] = function transitional(validator: false | undefined | ValidatorFn, version?: string, message?: string): ValidatorFn {
|
|
28
28
|
function formatMessage(opt: string, desc: string): string {
|
|
29
29
|
return (
|
|
30
30
|
"[Faxios v" +
|
|
@@ -80,7 +80,7 @@ const deprecatedWarnings: Record<string, boolean | undefined> = {};
|
|
|
80
80
|
* @returns {object}
|
|
81
81
|
*/
|
|
82
82
|
|
|
83
|
-
function assertOptions(options: unknown, schema: Record<string,
|
|
83
|
+
function assertOptions(options: unknown, schema: Record<string, undefined | ValidatorFn>, allowUnknown: boolean): void {
|
|
84
84
|
if (typeof options !== "object") {
|
|
85
85
|
throw new FaxiosError("options must be an object", FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
86
86
|
}
|
package/src/lib/platform.ts
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
import FaxiosURLSearchParams from "./helpers/FaxiosURLSearchParams.js";
|
|
4
4
|
|
|
5
5
|
const _globalThis = globalThis as {
|
|
6
|
-
|
|
6
|
+
Blob?: new (...args: Array<unknown>) => object;
|
|
7
7
|
document?: unknown;
|
|
8
|
+
FormData?: new () => object;
|
|
8
9
|
navigator?: { product?: string; userAgent?: string; };
|
|
9
|
-
WorkerGlobalScope?: unknown;
|
|
10
10
|
self?: { importScripts?: unknown; };
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
window?: { location?: { href?: string; }; };
|
|
12
|
+
WorkerGlobalScope?: unknown;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const hasBrowserEnv = typeof _globalThis.window !== "undefined" && typeof _globalThis.document !== "undefined";
|
|
@@ -13,10 +13,6 @@ import { definePlugin } from "./definePlugin.js";
|
|
|
13
13
|
export type AuthBearerCapability = { auth: { getToken: () => Promise<string>; }; };
|
|
14
14
|
|
|
15
15
|
export type AuthBearerOptions = {
|
|
16
|
-
/** Replace a header the request already has. Default `false`: an explicit header is kept. */
|
|
17
|
-
overwrite?: boolean;
|
|
18
|
-
/** Put before the token, separated by a space. Default `"Bearer"`; `""` sends the bare token. */
|
|
19
|
-
scheme?: string;
|
|
20
16
|
/** The header to set. Default `"Authorization"`. */
|
|
21
17
|
header?: string;
|
|
22
18
|
/**
|
|
@@ -24,6 +20,10 @@ export type AuthBearerOptions = {
|
|
|
24
20
|
* with an absolute `baseURL`, only its origin; otherwise only relative request URLs.
|
|
25
21
|
*/
|
|
26
22
|
origins?: Array<string>;
|
|
23
|
+
/** Replace a header the request already has. Default `false`: an explicit header is kept. */
|
|
24
|
+
overwrite?: boolean;
|
|
25
|
+
/** Put before the token, separated by a space. Default `"Bearer"`; `""` sends the bare token. */
|
|
26
|
+
scheme?: string;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
// A header name is a token (RFC 9110 §5.6.2).
|
|
@@ -31,7 +31,7 @@ const TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
|
31
31
|
|
|
32
32
|
// A bare origin, optionally with a trailing slash. A path would look like a narrower limit than
|
|
33
33
|
// the origin the token is really sent to.
|
|
34
|
-
function originOf(value: unknown):
|
|
34
|
+
function originOf(value: unknown): null | string {
|
|
35
35
|
if (typeof value !== "string") return null;
|
|
36
36
|
try {
|
|
37
37
|
const { origin } = new URL(value);
|
|
@@ -56,7 +56,7 @@ const own = <T extends object, K extends keyof T>(source: T, key: K): T[K] | und
|
|
|
56
56
|
// that leaves the page (`//host`, `/\host`) is told apart from one that stays on it.
|
|
57
57
|
const SAME_ORIGIN = "https://same-origin.invalid";
|
|
58
58
|
|
|
59
|
-
function resolvedOrigin(url: string, base: string | undefined):
|
|
59
|
+
function resolvedOrigin(url: string, base: string | undefined): null | string {
|
|
60
60
|
try {
|
|
61
61
|
return new URL(url, base).origin;
|
|
62
62
|
}
|
|
@@ -72,7 +72,7 @@ function pageHref(): string | undefined {
|
|
|
72
72
|
return undefined;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function sendsTokenTo(config: InternalFaxiosRequestConfig, origins: ReadonlyArray<string>
|
|
75
|
+
function sendsTokenTo(config: InternalFaxiosRequestConfig, origins: null | ReadonlyArray<string>): boolean {
|
|
76
76
|
const baseURL = own(config, "baseURL");
|
|
77
77
|
const url = buildFullPath(baseURL, own(config, "url"), own(config, "allowAbsoluteUrls"), config);
|
|
78
78
|
if (origins) {
|
|
@@ -91,8 +91,12 @@ function sendsTokenTo(config: InternalFaxiosRequestConfig, origins: ReadonlyArra
|
|
|
91
91
|
* can ask for a token too. A header the request already has is kept unless `overwrite` is set.
|
|
92
92
|
* The token goes only to the `baseURL` origin when `baseURL` is absolute, otherwise only to
|
|
93
93
|
* relative request URLs; `origins` replaces that rule.
|
|
94
|
+
*
|
|
95
|
+
* When `getToken` gives anything other than a non-empty string (`null`, `undefined`, `""`, a
|
|
96
|
+
* number, an object), the request is sent without the header, so a signed-out state never sends
|
|
97
|
+
* `Bearer null`. The parameter type stays `string`: the guard is for JavaScript callers and casts.
|
|
94
98
|
*/
|
|
95
|
-
export function authBearer(getToken: () => string |
|
|
99
|
+
export function authBearer(getToken: () => Promise<string> | string, options: AuthBearerOptions = {}): FaxiosPlugin<{ provides: AuthBearerCapability; }> {
|
|
96
100
|
if (typeof getToken !== "function") {
|
|
97
101
|
throw new FaxiosError("authBearer: getToken must be a function", FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
98
102
|
}
|
|
@@ -112,7 +116,8 @@ export function authBearer(getToken: () => string | Promise<string>, options: Au
|
|
|
112
116
|
provides: { auth },
|
|
113
117
|
middleware: async (ctx, next) => {
|
|
114
118
|
if ((!overwrite && ctx.config.headers.has(header)) || !sendsTokenTo(ctx.config, origins)) return next(ctx);
|
|
115
|
-
const token = await auth.getToken();
|
|
119
|
+
const token: unknown = await auth.getToken();
|
|
120
|
+
if (typeof token !== "string" || token === "") return next(ctx);
|
|
116
121
|
// On a copy: middleware that calls next() again (retry installed before this plugin) comes
|
|
117
122
|
// back through here without the header, so every try asks for a fresh token.
|
|
118
123
|
const headers = new FaxiosHeaders(ctx.config.headers).set(header, scheme ? `${scheme} ${token}` : token, true);
|
|
@@ -19,15 +19,15 @@ type RequiredCapabilities<TCapabilities, TProvides> = [keyof TProvides] extends
|
|
|
19
19
|
*/
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- `{}`: no capabilities or options, as in FaxiosContext
|
|
21
21
|
export function definePlugin<TProvides = {}, TOptions = {}, TCapabilities = TProvides>(plugin: {
|
|
22
|
+
middleware: FaxiosMiddleware<TOptions, TCapabilities>;
|
|
22
23
|
name: string;
|
|
23
24
|
// The middleware reads the capabilities it provides from ctx too, so they must have the types
|
|
24
25
|
// its context declares. NoInfer: TProvides is inferred from the value alone.
|
|
25
26
|
provides?: TProvides & NoInfer<Pick<TCapabilities, keyof TProvides & keyof TCapabilities>>;
|
|
26
|
-
middleware: FaxiosMiddleware<TOptions, TCapabilities>;
|
|
27
27
|
}): FaxiosPlugin<{
|
|
28
|
-
requires: RequiredCapabilities<TCapabilities, TProvides>;
|
|
29
|
-
provides: Slot<TProvides>;
|
|
30
28
|
options: Slot<TOptions>;
|
|
29
|
+
provides: Slot<TProvides>;
|
|
30
|
+
requires: RequiredCapabilities<TCapabilities, TProvides>;
|
|
31
31
|
}> {
|
|
32
32
|
// Only the types change: use() reads the fields at install time, as it does for any plugin.
|
|
33
33
|
return plugin as never;
|
package/src/lib/plugins/retry.ts
CHANGED
|
@@ -55,37 +55,36 @@ function assertRetryOptions(value: unknown, config?: InternalFaxiosRequestConfig
|
|
|
55
55
|
export type RetryOptions = {
|
|
56
56
|
/** Total tries, counting the first request. Default 3. */
|
|
57
57
|
attempts?: number;
|
|
58
|
-
/**
|
|
59
|
-
* Whether the try that just failed should be retried. `attempt` counts from 1.
|
|
60
|
-
* Default: network errors, timeouts and the `statuses`; never a cancellation. A custom `retryOn`
|
|
61
|
-
* replaces that whole predicate, so `statuses` is ignored when both are set. retryOn decides
|
|
62
|
-
* only within `methods`; to retry POST, list it in `methods`.
|
|
63
|
-
*/
|
|
64
|
-
retryOn?: (error: unknown, attempt: number) => boolean;
|
|
65
|
-
/**
|
|
66
|
-
* Response statuses the default predicate retries. Default 408, 429, 500, 502, 503 and 504.
|
|
67
|
-
* Ignored when `retryOn` is set.
|
|
68
|
-
*/
|
|
69
|
-
statuses?: ReadonlyArray<number>;
|
|
70
58
|
/**
|
|
71
59
|
* Milliseconds to wait before the next try, or a function of the try that failed. Default 100ms,
|
|
72
60
|
* doubling. Capped at `maxDelay`, then jittered. A 429 or 503 with a `Retry-After` header waits
|
|
73
61
|
* that long instead, unless `respectRetryAfter` is `false`.
|
|
74
62
|
*/
|
|
75
|
-
delay?:
|
|
76
|
-
/** The most the computed backoff waits, in milliseconds. Default 30 000. Doesn't apply to `Retry-After`. */
|
|
77
|
-
maxDelay?: number;
|
|
63
|
+
delay?: ((attempt: number, error: unknown) => number) | number;
|
|
78
64
|
/**
|
|
79
65
|
* `"full"` (default) waits a random time between 0 and the capped backoff, so clients that failed
|
|
80
66
|
* together don't retry together; `"none"` waits the capped backoff. Doesn't apply to `Retry-After`.
|
|
81
67
|
*/
|
|
82
68
|
jitter?: "full" | "none";
|
|
69
|
+
/** The most the computed backoff waits, in milliseconds. Default 30 000. Doesn't apply to `Retry-After`. */
|
|
70
|
+
maxDelay?: number;
|
|
83
71
|
/**
|
|
84
72
|
* The longest `Retry-After` (seconds or an HTTP date, on a 429 or 503) to wait, in milliseconds.
|
|
85
73
|
* Default 300 000 (5 minutes). A longer one rejects with the response's error instead of retrying.
|
|
86
74
|
* Ignored when `respectRetryAfter` is `false`.
|
|
87
75
|
*/
|
|
88
76
|
maxRetryAfter?: number;
|
|
77
|
+
/**
|
|
78
|
+
* HTTP methods that may be retried, in any case. Default: GET, HEAD, OPTIONS, PUT, DELETE and
|
|
79
|
+
* QUERY. POST and PATCH aren't idempotent, so add them only if your API dedupes repeats. A request
|
|
80
|
+
* whose method isn't listed is never retried, whatever `retryOn` or `statuses` say.
|
|
81
|
+
*/
|
|
82
|
+
methods?: ReadonlyArray<Method>;
|
|
83
|
+
/**
|
|
84
|
+
* Called before each wait with the error, the try that failed and the wait in milliseconds. If it
|
|
85
|
+
* throws, the request rejects with that error and stops retrying.
|
|
86
|
+
*/
|
|
87
|
+
onRetry?: (error: unknown, attempt: number, delayMs: number) => void;
|
|
89
88
|
/**
|
|
90
89
|
* Whether a 429 or 503's `Retry-After` header sets the wait. Default `true`. With `false`, every
|
|
91
90
|
* retry waits the computed backoff and `maxRetryAfter` doesn't apply; which requests retry doesn't
|
|
@@ -93,20 +92,21 @@ export type RetryOptions = {
|
|
|
93
92
|
*/
|
|
94
93
|
respectRetryAfter?: boolean;
|
|
95
94
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
95
|
+
* Whether the try that just failed should be retried. `attempt` counts from 1.
|
|
96
|
+
* Default: network errors, timeouts and the `statuses`; never a cancellation. A custom `retryOn`
|
|
97
|
+
* replaces that whole predicate, so `statuses` is ignored when both are set. retryOn decides
|
|
98
|
+
* only within `methods`; to retry POST, list it in `methods`.
|
|
98
99
|
*/
|
|
99
|
-
|
|
100
|
+
retryOn?: (error: unknown, attempt: number) => boolean;
|
|
100
101
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* whose method isn't listed is never retried, whatever `retryOn` or `statuses` say.
|
|
102
|
+
* Response statuses the default predicate retries. Default 408, 429, 500, 502, 503 and 504.
|
|
103
|
+
* Ignored when `retryOn` is set.
|
|
104
104
|
*/
|
|
105
|
-
|
|
105
|
+
statuses?: ReadonlyArray<number>;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
/** Per-request override: `false` turns retries off, an object overrides the plugin's options. */
|
|
109
|
-
export type RetryRequestOptions = { retry?:
|
|
109
|
+
export type RetryRequestOptions = { retry?: false | RetryOptions; };
|
|
110
110
|
|
|
111
111
|
function retryByDefault(error: unknown, statuses: ReadonlyArray<number>): boolean {
|
|
112
112
|
if (isCancel(error) || !(error instanceof FaxiosError)) return false;
|
|
@@ -164,13 +164,13 @@ function retriesMethod(config: InternalFaxiosRequestConfig, perRequest: RetryOpt
|
|
|
164
164
|
|
|
165
165
|
type Policy = {
|
|
166
166
|
attempts: number;
|
|
167
|
-
|
|
168
|
-
delay: number | ((attempt: number, error: unknown) => number);
|
|
169
|
-
maxDelay: number;
|
|
167
|
+
delay: ((attempt: number, error: unknown) => number) | number;
|
|
170
168
|
jitter: "full" | "none";
|
|
169
|
+
maxDelay: number;
|
|
171
170
|
maxRetryAfter: number;
|
|
172
|
-
respectRetryAfter: boolean;
|
|
173
171
|
onRetry: ((error: unknown, attempt: number, delayMs: number) => void) | undefined;
|
|
172
|
+
respectRetryAfter: boolean;
|
|
173
|
+
retryOn: (error: unknown, attempt: number) => boolean;
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
// Per field, request first: no merged object, and only own properties count.
|
|
@@ -11,17 +11,17 @@ import { definePlugin } from "./definePlugin.js";
|
|
|
11
11
|
* headers or body, so an event is safe to log.
|
|
12
12
|
*/
|
|
13
13
|
export type TimingEvent = {
|
|
14
|
-
/** The request method, upper-cased (`"GET"`). */
|
|
15
|
-
method: string;
|
|
16
|
-
/** The full request URL without its query string and fragment. */
|
|
17
|
-
url: string;
|
|
18
14
|
/**
|
|
19
15
|
* The try number from `retry`: each try's when installed after it, the final try's when
|
|
20
16
|
* installed before it. Absent without `retry`.
|
|
21
17
|
*/
|
|
22
18
|
attempt?: number;
|
|
23
19
|
durationMs: number;
|
|
24
|
-
|
|
20
|
+
/** The request method, upper-cased (`"GET"`). */
|
|
21
|
+
method: string;
|
|
22
|
+
/** The full request URL without its query string and fragment. */
|
|
23
|
+
url: string;
|
|
24
|
+
} & ({ error: unknown; status?: undefined; } | { error?: undefined; status: number; });
|
|
25
25
|
|
|
26
26
|
const now = () => (typeof performance === "undefined" ? Date.now() : performance.now());
|
|
27
27
|
|
|
@@ -13,12 +13,12 @@ export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
|
13
13
|
|
|
14
14
|
export declare namespace StandardSchemaV1 {
|
|
15
15
|
interface Props<Input = unknown, Output = Input> {
|
|
16
|
-
readonly version: 1;
|
|
17
|
-
readonly vendor: string;
|
|
18
16
|
readonly types?: Types<Input, Output>;
|
|
19
17
|
readonly validate: (
|
|
20
18
|
value: unknown
|
|
21
|
-
) => Result<Output
|
|
19
|
+
) => Promise<Result<Output>> | Result<Output>;
|
|
20
|
+
readonly vendor: string;
|
|
21
|
+
readonly version: 1;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface Types<Input = unknown, Output = Input> {
|
|
@@ -26,11 +26,11 @@ export declare namespace StandardSchemaV1 {
|
|
|
26
26
|
readonly output: Output;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
type Result<Output> = SuccessResult<Output
|
|
29
|
+
type Result<Output> = FailureResult | SuccessResult<Output>;
|
|
30
30
|
|
|
31
31
|
interface SuccessResult<Output> {
|
|
32
|
-
readonly value: Output;
|
|
33
32
|
readonly issues?: undefined;
|
|
33
|
+
readonly value: Output;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
interface FailureResult {
|
|
@@ -40,7 +40,7 @@ export declare namespace StandardSchemaV1 {
|
|
|
40
40
|
interface Issue {
|
|
41
41
|
readonly message: string;
|
|
42
42
|
readonly path?:
|
|
43
|
-
| ReadonlyArray<
|
|
43
|
+
| ReadonlyArray<PathSegment | PropertyKey>
|
|
44
44
|
| undefined;
|
|
45
45
|
}
|
|
46
46
|
|