@gcmdev/faxios 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -1774
- package/dist/index.d.ts +8 -6
- package/dist/index.js.map +1 -1
- package/dist/lib/adapters/fetch.js +23 -3
- package/dist/lib/adapters/fetch.js.map +1 -1
- package/dist/lib/cancel/isCancel.d.ts +2 -1
- package/dist/lib/cancel/isCancel.js +3 -0
- package/dist/lib/cancel/isCancel.js.map +1 -1
- package/dist/lib/core/Faxios.d.ts +5 -17
- package/dist/lib/core/Faxios.js +144 -76
- package/dist/lib/core/Faxios.js.map +1 -1
- package/dist/lib/core/FaxiosError.d.ts +18 -6
- package/dist/lib/core/FaxiosError.js +16 -1
- package/dist/lib/core/FaxiosError.js.map +1 -1
- package/dist/lib/core/FaxiosHeaders.d.ts +11 -8
- package/dist/lib/core/FaxiosHeaders.js +1 -0
- package/dist/lib/core/FaxiosHeaders.js.map +1 -1
- package/dist/lib/core/define.d.ts +2 -2
- package/dist/lib/core/define.js.map +1 -1
- package/dist/lib/core/dispatchRequest.js +4 -0
- package/dist/lib/core/dispatchRequest.js.map +1 -1
- package/dist/lib/core/prepareRequest.js +1 -20
- package/dist/lib/core/prepareRequest.js.map +1 -1
- package/dist/lib/core/route.d.ts +2 -2
- package/dist/lib/defaults/index.js +3 -3
- package/dist/lib/defaults/index.js.map +1 -1
- package/dist/lib/defaults/transitional.d.ts +0 -1
- package/dist/lib/defaults/transitional.js +0 -1
- package/dist/lib/defaults/transitional.js.map +1 -1
- package/dist/lib/env/data.d.ts +1 -1
- package/dist/lib/env/data.js +1 -1
- package/dist/lib/env/data.js.map +1 -1
- package/dist/lib/faxios.d.ts +94 -48
- package/dist/lib/faxios.js +14 -2
- package/dist/lib/faxios.js.map +1 -1
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts +2 -1
- package/dist/lib/helpers/FaxiosURLSearchParams.js.map +1 -1
- package/dist/lib/helpers/buildURL.js +7 -1
- package/dist/lib/helpers/buildURL.js.map +1 -1
- package/dist/lib/helpers/cloneConfig.d.ts +3 -0
- package/dist/lib/helpers/cloneConfig.js +21 -0
- package/dist/lib/helpers/cloneConfig.js.map +1 -0
- package/dist/lib/helpers/composeSignals.js +8 -1
- package/dist/lib/helpers/composeSignals.js.map +1 -1
- package/dist/lib/helpers/isFaxiosError.d.ts +7 -2
- package/dist/lib/helpers/isFaxiosError.js +5 -1
- package/dist/lib/helpers/isFaxiosError.js.map +1 -1
- package/dist/lib/platform.d.ts +1 -1
- package/dist/lib/plugins/attempt.d.ts +1 -0
- package/dist/lib/plugins/attempt.js +4 -0
- package/dist/lib/plugins/attempt.js.map +1 -0
- package/dist/lib/plugins/authBearer.d.ts +30 -0
- package/dist/lib/plugins/authBearer.js +99 -0
- package/dist/lib/plugins/authBearer.js.map +1 -0
- package/dist/lib/plugins/definePlugin.d.ts +21 -0
- package/dist/lib/plugins/definePlugin.js +14 -0
- package/dist/lib/plugins/definePlugin.js.map +1 -0
- package/dist/lib/plugins/retry.d.ts +65 -0
- package/dist/lib/plugins/retry.js +168 -0
- package/dist/lib/plugins/retry.js.map +1 -0
- package/dist/lib/plugins/timing.d.ts +31 -0
- package/dist/lib/plugins/timing.js +53 -0
- package/dist/lib/plugins/timing.js.map +1 -0
- package/dist/lib/types.d.ts +113 -34
- package/package.json +41 -19
- package/skills/configure-requests/SKILL.md +338 -0
- package/skills/configure-requests/references/axios-differences.md +67 -0
- package/skills/configure-requests/references/plugins.md +110 -0
- package/LICENSE +0 -7
- package/dist/lib/core/InterceptorManager.d.ts +0 -9
- package/dist/lib/core/InterceptorManager.js +0 -28
- package/dist/lib/core/InterceptorManager.js.map +0 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// @ts-self-types="./retry.d.ts" — required for Deno: maps built .js to adjacent .d.ts in dist/
|
|
2
|
+
import CanceledError from "../cancel/CanceledError.js";
|
|
3
|
+
import isCancel from "../cancel/isCancel.js";
|
|
4
|
+
import FaxiosError from "../core/FaxiosError.js";
|
|
5
|
+
import validator from "../helpers/validator.js";
|
|
6
|
+
import utils from "../utils.js";
|
|
7
|
+
import { RETRY_ATTEMPT } from "./attempt.js";
|
|
8
|
+
import { definePlugin } from "./definePlugin.js";
|
|
9
|
+
// Config values are read as own properties only (repo rule for possibly untrusted input).
|
|
10
|
+
const ownValue = (source, key) => (utils.hasOwnProp(source, key) ? source[key] : undefined);
|
|
11
|
+
const isWait = (ms) => typeof ms === "number" && Number.isFinite(ms) && ms >= 0;
|
|
12
|
+
// An HTTP method is a token (RFC 9110 §9.1, §5.6.2); anything else can never match a request.
|
|
13
|
+
const TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
14
|
+
const isStatus = (value) => Number.isInteger(value) && value >= 100 && value <= 599;
|
|
15
|
+
// Bounded on purpose: NaN or Infinity would make a failing request loop forever.
|
|
16
|
+
const optionsSchema = {
|
|
17
|
+
attempts: (value) => (Number.isInteger(value) && value >= 1) || "an integer of at least 1",
|
|
18
|
+
retryOn: validator.validators["function"],
|
|
19
|
+
delay: (value) => isWait(value) || typeof value === "function" || "a finite number of at least 0 or a function",
|
|
20
|
+
methods: (value) => (Array.isArray(value) && value.every(m => typeof m === "string" && TOKEN.test(m))) || "an array of HTTP method names",
|
|
21
|
+
statuses: (value) => (Array.isArray(value) && value.every(isStatus)) || "an array of HTTP status codes (100-599)",
|
|
22
|
+
maxRetryAfter: (value) => isWait(value) || "a finite number of milliseconds of at least 0",
|
|
23
|
+
maxDelay: (value) => isWait(value) || "a finite number of milliseconds of at least 0",
|
|
24
|
+
jitter: (value) => value === "full" || value === "none" || "\"full\" or \"none\"",
|
|
25
|
+
respectRetryAfter: validator.validators["boolean"],
|
|
26
|
+
onRetry: validator.validators["function"],
|
|
27
|
+
};
|
|
28
|
+
// Transient failures: timeout, rate limit, and server errors that may clear on their own. 501 and
|
|
29
|
+
// 505 mean the server will never handle the request, so they aren't here.
|
|
30
|
+
const DEFAULT_STATUSES = [408, 429, 500, 502, 503, 504];
|
|
31
|
+
// Statuses whose Retry-After says when to try again (RFC 9110 §10.2.3).
|
|
32
|
+
const RETRY_AFTER_STATUSES = [429, 503];
|
|
33
|
+
// Methods whose repeat has the same effect as one request (RFC 9110 §9.2.2, plus QUERY).
|
|
34
|
+
const IDEMPOTENT_METHODS = ["get", "head", "options", "put", "delete", "query"];
|
|
35
|
+
// One check for both the factory argument and a per-request override.
|
|
36
|
+
function assertRetryOptions(value, config) {
|
|
37
|
+
if (value === null || typeof value !== "object") {
|
|
38
|
+
throw new FaxiosError("retry options must be an object", FaxiosError.ERR_BAD_OPTION_VALUE, config);
|
|
39
|
+
}
|
|
40
|
+
validator.assertOptions(value, optionsSchema, false);
|
|
41
|
+
}
|
|
42
|
+
function retryByDefault(error, statuses) {
|
|
43
|
+
if (isCancel(error) || !(error instanceof FaxiosError))
|
|
44
|
+
return false;
|
|
45
|
+
if (error.code === FaxiosError.ERR_NETWORK || error.code === FaxiosError.ETIMEDOUT)
|
|
46
|
+
return true;
|
|
47
|
+
const status = error.response?.status;
|
|
48
|
+
return status !== undefined && statuses.includes(status);
|
|
49
|
+
}
|
|
50
|
+
// The wait a 429 or 503 asks for, in milliseconds: delay-seconds or an HTTP date (a past date
|
|
51
|
+
// means now). undefined when there's no usable header, so the computed backoff applies.
|
|
52
|
+
function retryAfterMs(error) {
|
|
53
|
+
if (!(error instanceof FaxiosError) || !error.response || !RETRY_AFTER_STATUSES.includes(error.response.status))
|
|
54
|
+
return undefined;
|
|
55
|
+
const value = error.response.headers.get("retry-after");
|
|
56
|
+
if (typeof value !== "string")
|
|
57
|
+
return undefined;
|
|
58
|
+
const trimmed = value.trim();
|
|
59
|
+
if (/^\d+$/.test(trimmed))
|
|
60
|
+
return Number(trimmed) * 1000;
|
|
61
|
+
const date = Date.parse(trimmed);
|
|
62
|
+
return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());
|
|
63
|
+
}
|
|
64
|
+
const doublingDelay = (attempt) => 100 * 2 ** (attempt - 1);
|
|
65
|
+
// A stream is consumed by the first try, so sending it again would send nothing.
|
|
66
|
+
// The same stream checks the fetch adapter uses: web ReadableStreams and Node-style pipe streams.
|
|
67
|
+
function canReplay(data) {
|
|
68
|
+
return !utils.isReadableStream?.(data) && !utils.isStream(data);
|
|
69
|
+
}
|
|
70
|
+
function wait(ms, signal, config) {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
if (signal?.aborted) {
|
|
73
|
+
reject(new CanceledError(null, config));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const onAbort = () => {
|
|
77
|
+
clearTimeout(timer);
|
|
78
|
+
signal?.removeEventListener?.("abort", onAbort);
|
|
79
|
+
reject(new CanceledError(null, config));
|
|
80
|
+
};
|
|
81
|
+
const timer = setTimeout(() => {
|
|
82
|
+
signal?.removeEventListener?.("abort", onAbort);
|
|
83
|
+
resolve();
|
|
84
|
+
}, ms);
|
|
85
|
+
signal?.addEventListener?.("abort", onAbort);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
// Each list is lower-cased right after it is validated: the plugin's once, a per-request override's
|
|
89
|
+
// on the request that carries it.
|
|
90
|
+
function retriesMethod(config, perRequest, pluginMethods) {
|
|
91
|
+
const requestMethods = perRequest ? ownValue(perRequest, "methods")?.map(m => m.toLowerCase()) : undefined;
|
|
92
|
+
const methods = requestMethods ?? pluginMethods ?? IDEMPOTENT_METHODS;
|
|
93
|
+
return methods.includes(String(ownValue(config, "method") ?? "get").toLowerCase());
|
|
94
|
+
}
|
|
95
|
+
// Per field, request first: no merged object, and only own properties count.
|
|
96
|
+
function resolvePolicy(options, perRequest) {
|
|
97
|
+
const setting = (key) => (perRequest ? ownValue(perRequest, key) : undefined) ?? ownValue(options, key);
|
|
98
|
+
const statuses = setting("statuses") ?? DEFAULT_STATUSES;
|
|
99
|
+
return {
|
|
100
|
+
attempts: setting("attempts") ?? 3,
|
|
101
|
+
retryOn: setting("retryOn") ?? ((error) => retryByDefault(error, statuses)),
|
|
102
|
+
delay: setting("delay") ?? doublingDelay,
|
|
103
|
+
maxDelay: setting("maxDelay") ?? 30_000,
|
|
104
|
+
jitter: setting("jitter") ?? "full",
|
|
105
|
+
maxRetryAfter: setting("maxRetryAfter") ?? 300_000,
|
|
106
|
+
respectRetryAfter: setting("respectRetryAfter") ?? true,
|
|
107
|
+
onRetry: setting("onRetry"),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// How long to wait before the next try, or undefined when the server's Retry-After is longer
|
|
111
|
+
// than the caller is willing to wait, so the error is rethrown.
|
|
112
|
+
function waitBeforeRetry(error, attempt, policy, config) {
|
|
113
|
+
const retryAfter = policy.respectRetryAfter ? retryAfterMs(error) : undefined;
|
|
114
|
+
if (retryAfter !== undefined)
|
|
115
|
+
return retryAfter > policy.maxRetryAfter ? undefined : retryAfter;
|
|
116
|
+
const computed = typeof policy.delay === "function" ? policy.delay(attempt, error) : policy.delay;
|
|
117
|
+
if (!isWait(computed)) {
|
|
118
|
+
throw new FaxiosError(`retry: delay must give a finite number of at least 0, got ${String(computed)}`, FaxiosError.ERR_BAD_OPTION_VALUE, config);
|
|
119
|
+
}
|
|
120
|
+
const capped = Math.min(computed, policy.maxDelay);
|
|
121
|
+
// Jitter only spreads out clients that failed together; it needs no unpredictability.
|
|
122
|
+
// eslint-disable-next-line sonarjs/pseudo-random -- see above
|
|
123
|
+
return policy.jitter === "full" ? Math.floor(Math.random() * capped) : capped;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Retries failed requests by calling `next` again. Each `next` dispatches its own copy of the
|
|
127
|
+
* config, so every try starts from the same input. Install it inside middleware that should run
|
|
128
|
+
* once per request (for example `timing` installed first measures all tries together).
|
|
129
|
+
*/
|
|
130
|
+
export function retry(options = {}) {
|
|
131
|
+
assertRetryOptions(options);
|
|
132
|
+
// Lower-cased once here, so requests compare names without re-casing the list.
|
|
133
|
+
const pluginMethods = ownValue(options, "methods")?.map(m => m.toLowerCase());
|
|
134
|
+
return definePlugin({
|
|
135
|
+
name: "retry",
|
|
136
|
+
middleware: async (ctx, next) => {
|
|
137
|
+
const perRequest = ownValue(ctx.config, "retry");
|
|
138
|
+
if (perRequest === false)
|
|
139
|
+
return next(ctx);
|
|
140
|
+
if (perRequest !== undefined)
|
|
141
|
+
assertRetryOptions(perRequest, ctx.config);
|
|
142
|
+
const policy = resolvePolicy(options, perRequest);
|
|
143
|
+
if (!retriesMethod(ctx.config, perRequest, pluginMethods))
|
|
144
|
+
return next(ctx);
|
|
145
|
+
// Tries run one after another on purpose: each must finish before deciding on the next.
|
|
146
|
+
for (let attempt = 1;; attempt++) {
|
|
147
|
+
// For timing: the try number, read after next() settles.
|
|
148
|
+
ctx.state[RETRY_ATTEMPT] = attempt;
|
|
149
|
+
try {
|
|
150
|
+
// eslint-disable-next-line no-await-in-loop -- see above
|
|
151
|
+
return await next(ctx);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (attempt >= policy.attempts || !canReplay(ownValue(ctx.config, "data")) || !policy.retryOn(error, attempt))
|
|
155
|
+
throw error;
|
|
156
|
+
const ms = waitBeforeRetry(error, attempt, policy, ctx.config);
|
|
157
|
+
if (ms === undefined)
|
|
158
|
+
throw error;
|
|
159
|
+
// A throwing hook rejects the request with its own error, before any wait.
|
|
160
|
+
policy.onRetry?.(error, attempt, ms);
|
|
161
|
+
// eslint-disable-next-line no-await-in-loop -- see above
|
|
162
|
+
await wait(ms, ownValue(ctx.config, "signal"), ctx.config);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../src/lib/plugins/retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FaxiosPlugin } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* What `timing` reports once per request: `status` on success, `error` on failure. No config,
|
|
4
|
+
* headers or body, so an event is safe to log.
|
|
5
|
+
*/
|
|
6
|
+
export type TimingEvent = {
|
|
7
|
+
/** The request method, upper-cased (`"GET"`). */
|
|
8
|
+
method: string;
|
|
9
|
+
/** The full request URL without its query string and fragment. */
|
|
10
|
+
url: string;
|
|
11
|
+
/**
|
|
12
|
+
* The try number from `retry`: each try's when installed after it, the final try's when
|
|
13
|
+
* installed before it. Absent without `retry`.
|
|
14
|
+
*/
|
|
15
|
+
attempt?: number;
|
|
16
|
+
durationMs: number;
|
|
17
|
+
} & ({
|
|
18
|
+
status: number;
|
|
19
|
+
error?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
status?: undefined;
|
|
22
|
+
error: unknown;
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Measures the time around `next` and calls `onTiming` once per request with the method, URL,
|
|
26
|
+
* duration and status or error. Installed before `retry`, the duration covers every try;
|
|
27
|
+
* installed after it, each try is reported. The response is returned unchanged. If `onTiming`
|
|
28
|
+
* throws while reporting a failure, the request still rejects with its own error; while
|
|
29
|
+
* reporting a success, its error rejects the request.
|
|
30
|
+
*/
|
|
31
|
+
export declare function timing(onTiming: (event: TimingEvent) => void): FaxiosPlugin;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// @ts-self-types="./timing.d.ts" — required for Deno: maps built .js to adjacent .d.ts in dist/
|
|
2
|
+
import buildFullPath from "../core/buildFullPath.js";
|
|
3
|
+
import utils from "../utils.js";
|
|
4
|
+
import { RETRY_ATTEMPT } from "./attempt.js";
|
|
5
|
+
import { definePlugin } from "./definePlugin.js";
|
|
6
|
+
const now = () => (typeof performance === "undefined" ? Date.now() : performance.now());
|
|
7
|
+
const own = (source, key) => (utils.hasOwnProp(source, key) ? source[key] : undefined);
|
|
8
|
+
// Read once the request settles, so a later middleware's changes to the config count. The query
|
|
9
|
+
// and fragment often carry secrets, and they'd make every URL a different metric label.
|
|
10
|
+
function describe({ config, state }) {
|
|
11
|
+
// Typed string, but undefined when the request has neither url nor baseURL.
|
|
12
|
+
const full = buildFullPath(own(config, "baseURL"), own(config, "url"), own(config, "allowAbsoluteUrls"));
|
|
13
|
+
const url = typeof full === "string" ? full : "";
|
|
14
|
+
const end = url.search(/[?#]/);
|
|
15
|
+
const attempt = state[RETRY_ATTEMPT];
|
|
16
|
+
return {
|
|
17
|
+
method: String(own(config, "method") ?? "get").toUpperCase(),
|
|
18
|
+
url: end === -1 ? url : url.slice(0, end),
|
|
19
|
+
...(typeof attempt === "number" ? { attempt } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Measures the time around `next` and calls `onTiming` once per request with the method, URL,
|
|
24
|
+
* duration and status or error. Installed before `retry`, the duration covers every try;
|
|
25
|
+
* installed after it, each try is reported. The response is returned unchanged. If `onTiming`
|
|
26
|
+
* throws while reporting a failure, the request still rejects with its own error; while
|
|
27
|
+
* reporting a success, its error rejects the request.
|
|
28
|
+
*/
|
|
29
|
+
export function timing(onTiming) {
|
|
30
|
+
return definePlugin({
|
|
31
|
+
name: "timing",
|
|
32
|
+
middleware: async (ctx, next) => {
|
|
33
|
+
const start = now();
|
|
34
|
+
let response;
|
|
35
|
+
try {
|
|
36
|
+
response = await next(ctx);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
try {
|
|
40
|
+
onTiming({ ...describe(ctx), durationMs: now() - start, error });
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// The request's own error matters more than the observer's; keep it.
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
// Outside the try, so an onTiming that throws isn't reported as a failed request.
|
|
48
|
+
onTiming({ ...describe(ctx), durationMs: now() - start, status: response.status });
|
|
49
|
+
return response;
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=timing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.js","sourceRoot":"","sources":["../../../src/lib/plugins/timing.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAEhG,OAAO,aAAa,iCAAiC;AAErD,OAAO,KAAK,oBAAoB;AAChC,OAAO,EAAE,aAAa,EAAE,qBAAqB;AAC7C,OAAO,EAAE,YAAY,EAAE,0BAA0B;AAmBjD,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC,OAAO,WAAW,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;AAExF,MAAM,GAAG,GAAG,CAAsC,MAAS,EAAE,GAAM,EAAoB,EAAE,CACvF,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAE5D,gGAAgG;AAChG,wFAAwF;AACxF,SAAS,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,EAAiB;IAChD,4EAA4E;IAC5E,MAAM,IAAI,GAAY,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAClH,MAAM,GAAG,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;IACrC,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE;QAC5D,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QACzC,GAAG,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpD,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CAAC,QAAsC;IAC3D,OAAO,YAAY,CAAC;QAClB,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAC9B,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;YACpB,IAAI,QAAQ,CAAC;YACb,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;YACD,OAAO,KAAK,EAAE,CAAC;gBACb,IAAI,CAAC;oBACH,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;gBACnE,CAAC;gBACD,MAAM,CAAC;oBACL,qEAAqE;gBACvE,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;YACD,kFAAkF;YAClF,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACnF,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FaxiosHeadersInstance } from "./core/FaxiosHeaders.js";
|
|
1
2
|
import type { StandardSchemaV1 } from "./types/standard-schema.js";
|
|
2
3
|
export type StringLiteralsOrString<Literals extends string> = Literals | (string & {});
|
|
3
4
|
export type FaxiosHeaderValue = string | Array<string> | number | boolean | null;
|
|
@@ -6,13 +7,25 @@ export interface RawFaxiosHeaders {
|
|
|
6
7
|
}
|
|
7
8
|
type UppercaseMethod = "GET" | "DELETE" | "HEAD" | "OPTIONS" | "POST" | "PUT" | "PATCH" | "PURGE" | "LINK" | "UNLINK" | "QUERY";
|
|
8
9
|
export type Method = UppercaseMethod | Lowercase<UppercaseMethod>;
|
|
9
|
-
type CommonRequestHeadersList = "Accept" | "
|
|
10
|
-
type ContentType =
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
type CommonRequestHeadersList = "Accept" | "Accept-Encoding" | "Accept-Language" | "Authorization" | "Cache-Control" | "Content-Encoding" | "Content-Length" | "If-Match" | "If-Modified-Since" | "If-None-Match" | "Range" | "User-Agent" | "X-Requested-With";
|
|
11
|
+
type ContentType = StringLiteralsOrString<"text/html" | "text/plain" | "multipart/form-data" | "application/json" | "application/x-www-form-urlencoded" | "application/octet-stream" | "application/problem+json" | "application/ld+json" | "text/event-stream" | "application/graphql-response+json" | "application/merge-patch+json" | "application/json-patch+json"> | Exclude<FaxiosHeaderValue, string>;
|
|
12
|
+
/**
|
|
13
|
+
* A header bag: header names mapped to header values. Any header name is
|
|
14
|
+
* accepted in any casing; common names and `Content-Type` values are suggested.
|
|
15
|
+
*
|
|
16
|
+
* - Strings, numbers and string arrays are sent (numbers and `true` as strings).
|
|
17
|
+
* - `undefined` drops a value inherited from defaults or an outer config;
|
|
18
|
+
* faxios may still set its own value (e.g. `Content-Type` for a JSON body).
|
|
19
|
+
* - `null` or `false` keeps the header off the request entirely; faxios does
|
|
20
|
+
* not fill it in, and later merges keep it off unless they force an overwrite.
|
|
21
|
+
*/
|
|
22
|
+
export type RawFaxiosRequestHeaders = {
|
|
23
|
+
[header: string]: FaxiosHeaderValue | undefined;
|
|
13
24
|
} & {
|
|
14
|
-
|
|
15
|
-
}
|
|
25
|
+
[Header in CommonRequestHeadersList]?: FaxiosHeaderValue;
|
|
26
|
+
} & {
|
|
27
|
+
"Content-Type"?: ContentType;
|
|
28
|
+
};
|
|
16
29
|
export type ResponseType = "arraybuffer" | "blob" | "document" | "json" | "text" | "stream" | "formdata" | "response";
|
|
17
30
|
type UppercaseResponseEncoding = "ASCII" | "ANSI" | "BINARY" | "BASE64" | "BASE64URL" | "HEX" | "LATIN1" | "UCS-2" | "UCS2" | "UTF-8" | "UTF8" | "UTF16LE";
|
|
18
31
|
export type responseEncoding = UppercaseResponseEncoding | Lowercase<UppercaseResponseEncoding>;
|
|
@@ -20,7 +33,6 @@ export interface TransitionalOptions {
|
|
|
20
33
|
silentJSONParsing?: boolean;
|
|
21
34
|
forcedJSONParsing?: boolean;
|
|
22
35
|
clarifyTimeoutError?: boolean;
|
|
23
|
-
legacyInterceptorReqResOrdering?: boolean;
|
|
24
36
|
advertiseZstdAcceptEncoding?: boolean;
|
|
25
37
|
validateStatusUndefinedResolves?: boolean;
|
|
26
38
|
}
|
|
@@ -51,10 +63,12 @@ export interface SerializerOptions {
|
|
|
51
63
|
dots?: boolean;
|
|
52
64
|
metaTokens?: boolean;
|
|
53
65
|
indexes?: boolean | null;
|
|
66
|
+
/** Maximum nesting depth to serialize; deeper objects throw `ERR_FORM_DATA_DEPTH_EXCEEDED`. `Infinity` disables the check. */
|
|
67
|
+
maxDepth?: number;
|
|
54
68
|
}
|
|
55
69
|
export type FormSerializerOptions = SerializerOptions;
|
|
56
70
|
export interface ParamEncoder {
|
|
57
|
-
(value:
|
|
71
|
+
(value: string, defaultEncoder: (value: string) => string): string;
|
|
58
72
|
}
|
|
59
73
|
export interface CustomParamsSerializer {
|
|
60
74
|
(params: Record<string, unknown>, options?: ParamsSerializerOptions): string;
|
|
@@ -82,10 +96,9 @@ export interface FaxiosBasicCredentials {
|
|
|
82
96
|
password: string;
|
|
83
97
|
}
|
|
84
98
|
type HeaderMatcher = string | RegExp | ((value: string, name: string) => boolean);
|
|
85
|
-
type HeaderRewrite = boolean | ((value: string, name: string) => boolean);
|
|
86
99
|
type HeaderGetResult = string | Array<string> | Record<string, string> | RegExpExecArray | true | null | undefined;
|
|
87
100
|
export type FaxiosRequestHeaders = Record<string, FaxiosHeaderValue> & {
|
|
88
|
-
set: (header: string | Record<string, unknown>, value?: FaxiosHeaderValue, rewrite?:
|
|
101
|
+
set: (header: string | Record<string, unknown>, value?: FaxiosHeaderValue, rewrite?: boolean) => FaxiosRequestHeaders;
|
|
89
102
|
get: (header: string, parser?: boolean | RegExp) => HeaderGetResult;
|
|
90
103
|
has: (header: string, matcher?: HeaderMatcher) => boolean;
|
|
91
104
|
delete: (header: string | Array<string>, matcher?: HeaderMatcher) => boolean;
|
|
@@ -93,22 +106,22 @@ export type FaxiosRequestHeaders = Record<string, FaxiosHeaderValue> & {
|
|
|
93
106
|
normalize: (format?: boolean) => FaxiosRequestHeaders;
|
|
94
107
|
concat: (...targets: Array<unknown>) => FaxiosRequestHeaders;
|
|
95
108
|
getContentType: (matcher?: HeaderMatcher) => HeaderGetResult;
|
|
96
|
-
setContentType: (value: FaxiosHeaderValue, rewrite?:
|
|
109
|
+
setContentType: (value: FaxiosHeaderValue, rewrite?: boolean) => FaxiosRequestHeaders;
|
|
97
110
|
hasContentType: (matcher?: HeaderMatcher) => boolean;
|
|
98
111
|
getContentLength: (matcher?: HeaderMatcher) => HeaderGetResult;
|
|
99
|
-
setContentLength: (value: FaxiosHeaderValue, rewrite?:
|
|
112
|
+
setContentLength: (value: FaxiosHeaderValue, rewrite?: boolean) => FaxiosRequestHeaders;
|
|
100
113
|
hasContentLength: (matcher?: HeaderMatcher) => boolean;
|
|
101
114
|
getAccept: (matcher?: HeaderMatcher) => HeaderGetResult;
|
|
102
|
-
setAccept: (value: FaxiosHeaderValue, rewrite?:
|
|
115
|
+
setAccept: (value: FaxiosHeaderValue, rewrite?: boolean) => FaxiosRequestHeaders;
|
|
103
116
|
hasAccept: (matcher?: HeaderMatcher) => boolean;
|
|
117
|
+
getAcceptEncoding: (matcher?: HeaderMatcher) => HeaderGetResult;
|
|
118
|
+
setAcceptEncoding: (value: FaxiosHeaderValue, rewrite?: boolean) => FaxiosRequestHeaders;
|
|
119
|
+
hasAcceptEncoding: (matcher?: HeaderMatcher) => boolean;
|
|
104
120
|
getUserAgent: (matcher?: HeaderMatcher) => HeaderGetResult;
|
|
105
|
-
setUserAgent: (value: FaxiosHeaderValue, rewrite?:
|
|
121
|
+
setUserAgent: (value: FaxiosHeaderValue, rewrite?: boolean) => FaxiosRequestHeaders;
|
|
106
122
|
hasUserAgent: (matcher?: HeaderMatcher) => boolean;
|
|
107
|
-
getContentEncoding: (matcher?: HeaderMatcher) => HeaderGetResult;
|
|
108
|
-
setContentEncoding: (value: FaxiosHeaderValue, rewrite?: HeaderRewrite) => FaxiosRequestHeaders;
|
|
109
|
-
hasContentEncoding: (matcher?: HeaderMatcher) => boolean;
|
|
110
123
|
getAuthorization: (matcher?: HeaderMatcher) => HeaderGetResult;
|
|
111
|
-
setAuthorization: (value: FaxiosHeaderValue, rewrite?:
|
|
124
|
+
setAuthorization: (value: FaxiosHeaderValue, rewrite?: boolean) => FaxiosRequestHeaders;
|
|
112
125
|
hasAuthorization: (matcher?: HeaderMatcher) => boolean;
|
|
113
126
|
toJSON: (asStrings?: boolean) => Record<string, FaxiosHeaderValue>;
|
|
114
127
|
};
|
|
@@ -125,7 +138,7 @@ export interface FaxiosRequestConfig<D = unknown> {
|
|
|
125
138
|
allowAbsoluteUrls?: boolean;
|
|
126
139
|
transformRequest?: FaxiosRequestTransformer | Array<FaxiosRequestTransformer>;
|
|
127
140
|
transformResponse?: FaxiosResponseTransformer | Array<FaxiosResponseTransformer>;
|
|
128
|
-
headers?:
|
|
141
|
+
headers?: FaxiosConfigHeaders;
|
|
129
142
|
params?: Record<string, unknown> | URLSearchParams;
|
|
130
143
|
paramsSerializer?: ParamsSerializerOptions | CustomParamsSerializer;
|
|
131
144
|
data?: D;
|
|
@@ -145,10 +158,12 @@ export interface FaxiosRequestConfig<D = unknown> {
|
|
|
145
158
|
transitional?: TransitionalOptions;
|
|
146
159
|
signal?: AbortSignal | GenericAbortSignal;
|
|
147
160
|
env?: {
|
|
148
|
-
|
|
161
|
+
/** Constructed with no arguments to serialize `multipart/form-data` payloads; `null` falls back to the global `FormData`. */
|
|
162
|
+
FormData?: (new () => object) | null;
|
|
149
163
|
fetch?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
150
|
-
|
|
151
|
-
|
|
164
|
+
/** `null` disables the constructor, e.g. when a custom `fetch` is incompatible with the global one. */
|
|
165
|
+
Request?: (new (input: string | URL | Request, init?: RequestInit) => unknown) | null;
|
|
166
|
+
Response?: (new (...args: Array<unknown>) => unknown) | null;
|
|
152
167
|
};
|
|
153
168
|
formSerializer?: FormSerializerOptions;
|
|
154
169
|
withXSRFToken?: boolean | ((config: InternalFaxiosRequestConfig) => boolean | undefined);
|
|
@@ -201,19 +216,83 @@ export interface HeadersDefaults {
|
|
|
201
216
|
export interface FaxiosDefaults<D = unknown> extends Omit<FaxiosRequestConfig<D>, "headers"> {
|
|
202
217
|
headers: HeadersDefaults;
|
|
203
218
|
}
|
|
204
|
-
|
|
205
|
-
|
|
219
|
+
/**
|
|
220
|
+
* The `headers` option on every request config and on `create()`: a header
|
|
221
|
+
* bag, method header groups (a header bag under `common` or a method name,
|
|
222
|
+
* applied only to requests with that method), both mixed in one object, or a
|
|
223
|
+
* `FaxiosHeaders` instance.
|
|
224
|
+
*/
|
|
225
|
+
export type FaxiosConfigHeaders = RawFaxiosRequestHeaders | Partial<HeadersDefaults> | FaxiosHeadersInstance;
|
|
226
|
+
/** Config accepted by `create()`; identical to a request config. */
|
|
227
|
+
export type CreateFaxiosDefaults<D = unknown> = FaxiosRequestConfig<D>;
|
|
228
|
+
interface ContextHeaders {
|
|
229
|
+
get headers(): FaxiosRequestHeaders;
|
|
230
|
+
set headers(value: FaxiosRequestHeaders | FaxiosHeadersInstance);
|
|
206
231
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
232
|
+
/**
|
|
233
|
+
* Per-request state passed through `.use()` middleware. Created once per
|
|
234
|
+
* request, after the request config is merged with the instance defaults.
|
|
235
|
+
*/
|
|
236
|
+
export interface FaxiosContext<TOptions = {}, TCapabilities = {}> {
|
|
237
|
+
/** The merged request config. `headers` is a `FaxiosHeaders` instance. */
|
|
238
|
+
config: Omit<InternalFaxiosRequestConfig, "headers"> & ContextHeaders & TOptions;
|
|
239
|
+
/** Null-prototype scratch space shared by middleware for this request only. */
|
|
240
|
+
state: Record<PropertyKey, unknown>;
|
|
241
|
+
/** Values provided by installed plugins, shared by every request of the instance. */
|
|
242
|
+
capabilities: TCapabilities;
|
|
210
243
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Runs the rest of the middleware chain, then dispatches a copy of `ctx.config`. It takes the same
|
|
246
|
+
* context type the middleware received, so a middleware can't pass on fewer capabilities or
|
|
247
|
+
* options than later middleware expects.
|
|
248
|
+
*/
|
|
249
|
+
export type FaxiosNext<TOptions = {}, TCapabilities = {}> = (ctx: FaxiosContext<TOptions, TCapabilities>) => Promise<FaxiosResponse>;
|
|
250
|
+
/**
|
|
251
|
+
* Onion-style lifecycle hook: code before `await next(ctx)` runs on the way in,
|
|
252
|
+
* code after it on the way out. Returning without calling `next` skips dispatch.
|
|
253
|
+
*/
|
|
254
|
+
export type FaxiosMiddleware<TOptions = {}, TCapabilities = {}> = (ctx: FaxiosContext<TOptions, TCapabilities>, next: FaxiosNext<TOptions, TCapabilities>) => Promise<FaxiosResponse>;
|
|
255
|
+
/**
|
|
256
|
+
* The type slots of a `FaxiosPlugin`, all optional: `requires` holds the capabilities that must be
|
|
257
|
+
* installed before it, `provides` the capabilities it adds to `ctx.capabilities`, and `options`
|
|
258
|
+
* the request options it adds to every config-taking member.
|
|
259
|
+
*/
|
|
260
|
+
export interface FaxiosPluginSpec {
|
|
261
|
+
requires?: unknown;
|
|
262
|
+
provides?: unknown;
|
|
263
|
+
options?: unknown;
|
|
218
264
|
}
|
|
265
|
+
type PluginSlot<TSpec, K extends keyof FaxiosPluginSpec> = K extends keyof TSpec ? Required<TSpec>[K] : unknown;
|
|
266
|
+
type OnlyPluginSlots<TSpec> = {
|
|
267
|
+
[K in Exclude<keyof TSpec, keyof FaxiosPluginSpec>]: never;
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* The parts of a plugin that `use()` reads its type parameters from. Internal: build plugins with
|
|
271
|
+
* `definePlugin()` or annotate them as `FaxiosPlugin`; `use()` infers `TRequires`, `TProvides`
|
|
272
|
+
* and `TOptions` through this.
|
|
273
|
+
*/
|
|
274
|
+
export interface FaxiosPluginBase<TRequires = unknown, TProvides = unknown, TOptions = unknown> {
|
|
275
|
+
name: string;
|
|
276
|
+
middleware: FaxiosMiddleware<TOptions, TRequires & TProvides>;
|
|
277
|
+
/** Internal, type-only marker that carries the type parameters for inference. Never set it. */
|
|
278
|
+
readonly "~plugin"?: {
|
|
279
|
+
readonly requires: TRequires;
|
|
280
|
+
readonly provides: TProvides;
|
|
281
|
+
readonly options: TOptions;
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Middleware bundled with the capabilities it requires and provides, typed by one
|
|
286
|
+
* `FaxiosPluginSpec`: `FaxiosPlugin<{ requires?; provides?; options? }>`. Declaring `provides`
|
|
287
|
+
* makes the `provides` value required, so a plugin can't claim a capability it never supplies.
|
|
288
|
+
*/
|
|
289
|
+
export type FaxiosPlugin<TSpec extends FaxiosPluginSpec & OnlyPluginSlots<TSpec> = {}> = FaxiosPluginBase<PluginSlot<TSpec, "requires">, PluginSlot<TSpec, "provides">, PluginSlot<TSpec, "options">> & ([keyof PluginSlot<TSpec, "provides">] extends [never] ? {
|
|
290
|
+
provides?: PluginSlot<TSpec, "provides">;
|
|
291
|
+
} : {
|
|
292
|
+
provides: PluginSlot<TSpec, "provides">;
|
|
293
|
+
});
|
|
294
|
+
/** What `use()` and `eject()` accept as a plugin: the base plus an optional `provides`. Internal. */
|
|
295
|
+
export type FaxiosPluginArgument<TRequires = unknown, TProvides = unknown, TOptions = unknown> = FaxiosPluginBase<TRequires, TProvides, TOptions> & {
|
|
296
|
+
provides?: TProvides;
|
|
297
|
+
};
|
|
219
298
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gcmdev/faxios",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Promise based, web-standard fetch HTTP client for the browser, Node.js, Deno and Bun",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"browser",
|
|
18
18
|
"rest",
|
|
19
19
|
"api",
|
|
20
|
-
"client"
|
|
20
|
+
"client",
|
|
21
|
+
"tanstack-intent"
|
|
21
22
|
],
|
|
22
23
|
"author": "Adrian Elton-Browning",
|
|
23
24
|
"contributors": [
|
|
@@ -40,20 +41,37 @@
|
|
|
40
41
|
"bugs": {
|
|
41
42
|
"url": "https://github.com/adrianbrowning/faxios/issues"
|
|
42
43
|
},
|
|
43
|
-
"homepage": "https://faxios
|
|
44
|
+
"homepage": "https://adrianbrowning.github.io/faxios/",
|
|
44
45
|
"files": [
|
|
45
|
-
"dist/"
|
|
46
|
+
"dist/",
|
|
47
|
+
"skills/configure-requests"
|
|
46
48
|
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "zshy",
|
|
51
|
+
"bp": "pnpm build && pnpm pack",
|
|
52
|
+
"version": "pnpm run build && git add package.json",
|
|
53
|
+
"docs:dev": "cd docs && npm run docs:dev",
|
|
54
|
+
"lint": "pnpm lint:ts && pnpm lint:esl && pnpm lint:fix",
|
|
55
|
+
"lint:ts": "tsc --noEmit -p tsconfig.check.json",
|
|
56
|
+
"lint:esl": "eslint --config eslint.config.ts \"**/*.{j,t}s{,x}\" --cache --cache-strategy content --max-warnings=0",
|
|
57
|
+
"lint:s": "eslint --config eslint.config.style.ts \"**/*.{j,t}s{,x}\" --cache --cache-strategy content --max-warnings=0",
|
|
58
|
+
"lint:fix": "pnpm lint:s --fix",
|
|
59
|
+
"lint:e18e": "pnpm dlx @e18e/cli analyze"
|
|
60
|
+
},
|
|
47
61
|
"devDependencies": {
|
|
48
|
-
"@gingacodemonkey/config": "^0.0.
|
|
62
|
+
"@gingacodemonkey/config": "^0.0.38",
|
|
49
63
|
"eslint": "^9.39.4",
|
|
50
64
|
"typescript": "^6.0.3",
|
|
51
|
-
"typescript-eslint": "^8.
|
|
65
|
+
"typescript-eslint": "^8.71.0",
|
|
52
66
|
"zshy": "latest"
|
|
53
67
|
},
|
|
54
68
|
"zshy": {
|
|
55
69
|
"exports": {
|
|
56
70
|
".": "./src/index.ts",
|
|
71
|
+
"./plugins": "./src/lib/plugins/definePlugin.ts",
|
|
72
|
+
"./plugins/auth-bearer": "./src/lib/plugins/authBearer.ts",
|
|
73
|
+
"./plugins/retry": "./src/lib/plugins/retry.ts",
|
|
74
|
+
"./plugins/timing": "./src/lib/plugins/timing.ts",
|
|
57
75
|
"./unsafe/core/buildFullPath.js": "./src/lib/core/buildFullPath.ts",
|
|
58
76
|
"./unsafe/helpers/isAbsoluteURL.js": "./src/lib/helpers/isAbsoluteURL.ts",
|
|
59
77
|
"./unsafe/helpers/buildURL.js": "./src/lib/helpers/buildURL.ts",
|
|
@@ -73,6 +91,22 @@
|
|
|
73
91
|
"types": "./dist/index.d.ts",
|
|
74
92
|
"default": "./dist/index.js"
|
|
75
93
|
},
|
|
94
|
+
"./plugins": {
|
|
95
|
+
"types": "./dist/lib/plugins/definePlugin.d.ts",
|
|
96
|
+
"default": "./dist/lib/plugins/definePlugin.js"
|
|
97
|
+
},
|
|
98
|
+
"./plugins/auth-bearer": {
|
|
99
|
+
"types": "./dist/lib/plugins/authBearer.d.ts",
|
|
100
|
+
"default": "./dist/lib/plugins/authBearer.js"
|
|
101
|
+
},
|
|
102
|
+
"./plugins/retry": {
|
|
103
|
+
"types": "./dist/lib/plugins/retry.d.ts",
|
|
104
|
+
"default": "./dist/lib/plugins/retry.js"
|
|
105
|
+
},
|
|
106
|
+
"./plugins/timing": {
|
|
107
|
+
"types": "./dist/lib/plugins/timing.d.ts",
|
|
108
|
+
"default": "./dist/lib/plugins/timing.js"
|
|
109
|
+
},
|
|
76
110
|
"./unsafe/core/buildFullPath.js": {
|
|
77
111
|
"types": "./dist/lib/core/buildFullPath.d.ts",
|
|
78
112
|
"default": "./dist/lib/core/buildFullPath.js"
|
|
@@ -94,17 +128,5 @@
|
|
|
94
128
|
"default": "./dist/lib/utils.js"
|
|
95
129
|
},
|
|
96
130
|
"./package.json": "./package.json"
|
|
97
|
-
},
|
|
98
|
-
"scripts": {
|
|
99
|
-
"build": "zshy",
|
|
100
|
-
"bp": "pnpm build && pnpm pack",
|
|
101
|
-
"version": "pnpm run build && git add package.json",
|
|
102
|
-
"docs:dev": "cd docs && npm run docs:dev",
|
|
103
|
-
"lint": "pnpm lint:ts; pnpm lint:fix",
|
|
104
|
-
"lint:ts": "tsc --noEmit -p tsconfig.check.json",
|
|
105
|
-
"lint:esl": "eslint --config eslint.config.ts \"**/*.{j,t}s{,x}\" --cache --cache-strategy content --max-warnings=0",
|
|
106
|
-
"lint:s": "eslint --config eslint.config.style.ts \"**/*.{j,t}s{,x}\" --cache --cache-strategy content --max-warnings=0",
|
|
107
|
-
"lint:fix": "pnpm lint:s --fix",
|
|
108
|
-
"lint:e18e": "pnpm dlx @e18e/cli analyze"
|
|
109
131
|
}
|
|
110
|
-
}
|
|
132
|
+
}
|