@gcmdev/faxios 0.1.0 → 0.2.1
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 +1 -0
- package/README.md +40 -1774
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/adapters/fetch-helpers.d.ts +1 -0
- package/dist/lib/adapters/fetch-helpers.d.ts.map +1 -0
- package/dist/lib/adapters/fetch.d.ts +1 -0
- package/dist/lib/adapters/fetch.d.ts.map +1 -0
- package/dist/lib/adapters/fetch.js +23 -3
- package/dist/lib/adapters/fetch.js.map +1 -1
- package/dist/lib/cancel/CanceledError.d.ts +1 -0
- package/dist/lib/cancel/CanceledError.d.ts.map +1 -0
- package/dist/lib/cancel/isCancel.d.ts +3 -1
- package/dist/lib/cancel/isCancel.d.ts.map +1 -0
- package/dist/lib/cancel/isCancel.js +3 -0
- package/dist/lib/cancel/isCancel.js.map +1 -1
- package/dist/lib/core/Faxios.d.ts +6 -17
- package/dist/lib/core/Faxios.d.ts.map +1 -0
- package/dist/lib/core/Faxios.js +144 -76
- package/dist/lib/core/Faxios.js.map +1 -1
- package/dist/lib/core/FaxiosError.d.ts +19 -6
- package/dist/lib/core/FaxiosError.d.ts.map +1 -0
- package/dist/lib/core/FaxiosError.js +16 -1
- package/dist/lib/core/FaxiosError.js.map +1 -1
- package/dist/lib/core/FaxiosHeaders.d.ts +12 -8
- package/dist/lib/core/FaxiosHeaders.d.ts.map +1 -0
- package/dist/lib/core/FaxiosHeaders.js +1 -0
- package/dist/lib/core/FaxiosHeaders.js.map +1 -1
- package/dist/lib/core/buildFullPath.d.ts +1 -0
- package/dist/lib/core/buildFullPath.d.ts.map +1 -0
- package/dist/lib/core/define.d.ts +19 -2
- package/dist/lib/core/define.d.ts.map +1 -0
- package/dist/lib/core/define.js.map +1 -1
- package/dist/lib/core/dispatchRequest.d.ts +1 -0
- package/dist/lib/core/dispatchRequest.d.ts.map +1 -0
- package/dist/lib/core/dispatchRequest.js +4 -0
- package/dist/lib/core/dispatchRequest.js.map +1 -1
- package/dist/lib/core/mergeConfig.d.ts +1 -0
- package/dist/lib/core/mergeConfig.d.ts.map +1 -0
- package/dist/lib/core/prepareRequest.d.ts +1 -0
- package/dist/lib/core/prepareRequest.d.ts.map +1 -0
- package/dist/lib/core/prepareRequest.js +1 -20
- package/dist/lib/core/prepareRequest.js.map +1 -1
- package/dist/lib/core/route.d.ts +11 -2
- package/dist/lib/core/route.d.ts.map +1 -0
- package/dist/lib/core/route.js.map +1 -1
- package/dist/lib/core/settle.d.ts +1 -0
- package/dist/lib/core/settle.d.ts.map +1 -0
- package/dist/lib/core/transformData.d.ts +1 -0
- package/dist/lib/core/transformData.d.ts.map +1 -0
- package/dist/lib/core/validateSchema.d.ts +1 -0
- package/dist/lib/core/validateSchema.d.ts.map +1 -0
- package/dist/lib/defaults/index.d.ts +1 -0
- package/dist/lib/defaults/index.d.ts.map +1 -0
- package/dist/lib/defaults/index.js +3 -3
- package/dist/lib/defaults/index.js.map +1 -1
- package/dist/lib/defaults/transitional.d.ts +1 -1
- package/dist/lib/defaults/transitional.d.ts.map +1 -0
- package/dist/lib/defaults/transitional.js +0 -1
- package/dist/lib/defaults/transitional.js.map +1 -1
- package/dist/lib/env/data.d.ts +2 -1
- package/dist/lib/env/data.d.ts.map +1 -0
- package/dist/lib/env/data.js +1 -1
- package/dist/lib/env/data.js.map +1 -1
- package/dist/lib/faxios.d.ts +115 -48
- package/dist/lib/faxios.d.ts.map +1 -0
- package/dist/lib/faxios.js +14 -2
- package/dist/lib/faxios.js.map +1 -1
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts +3 -1
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts.map +1 -0
- package/dist/lib/helpers/FaxiosURLSearchParams.js.map +1 -1
- package/dist/lib/helpers/HttpStatusCode.d.ts +1 -0
- package/dist/lib/helpers/HttpStatusCode.d.ts.map +1 -0
- package/dist/lib/helpers/buildURL.d.ts +1 -0
- package/dist/lib/helpers/buildURL.d.ts.map +1 -0
- package/dist/lib/helpers/buildURL.js +7 -1
- package/dist/lib/helpers/buildURL.js.map +1 -1
- package/dist/lib/helpers/cloneConfig.d.ts +4 -0
- package/dist/lib/helpers/cloneConfig.d.ts.map +1 -0
- package/dist/lib/helpers/cloneConfig.js +21 -0
- package/dist/lib/helpers/cloneConfig.js.map +1 -0
- package/dist/lib/helpers/combineURLs.d.ts +1 -0
- package/dist/lib/helpers/combineURLs.d.ts.map +1 -0
- package/dist/lib/helpers/composeSignals.d.ts +1 -0
- package/dist/lib/helpers/composeSignals.d.ts.map +1 -0
- package/dist/lib/helpers/composeSignals.js +8 -1
- package/dist/lib/helpers/composeSignals.js.map +1 -1
- package/dist/lib/helpers/cookies.d.ts +1 -0
- package/dist/lib/helpers/cookies.d.ts.map +1 -0
- package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts +1 -0
- package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts.map +1 -0
- package/dist/lib/helpers/formDataToJSON.d.ts +1 -0
- package/dist/lib/helpers/formDataToJSON.d.ts.map +1 -0
- package/dist/lib/helpers/isAbsoluteURL.d.ts +1 -0
- package/dist/lib/helpers/isAbsoluteURL.d.ts.map +1 -0
- package/dist/lib/helpers/isFaxiosError.d.ts +8 -2
- package/dist/lib/helpers/isFaxiosError.d.ts.map +1 -0
- package/dist/lib/helpers/isFaxiosError.js +5 -1
- package/dist/lib/helpers/isFaxiosError.js.map +1 -1
- package/dist/lib/helpers/isURLSameOrigin.d.ts +1 -0
- package/dist/lib/helpers/isURLSameOrigin.d.ts.map +1 -0
- package/dist/lib/helpers/parseHeaders.d.ts +1 -0
- package/dist/lib/helpers/parseHeaders.d.ts.map +1 -0
- package/dist/lib/helpers/progressEventReducer.d.ts +1 -0
- package/dist/lib/helpers/progressEventReducer.d.ts.map +1 -0
- package/dist/lib/helpers/sanitizeHeaderValue.d.ts +1 -0
- package/dist/lib/helpers/sanitizeHeaderValue.d.ts.map +1 -0
- package/dist/lib/helpers/speedometer.d.ts +1 -0
- package/dist/lib/helpers/speedometer.d.ts.map +1 -0
- package/dist/lib/helpers/substitutePathParams.d.ts +1 -0
- package/dist/lib/helpers/substitutePathParams.d.ts.map +1 -0
- package/dist/lib/helpers/throttle.d.ts +1 -0
- package/dist/lib/helpers/throttle.d.ts.map +1 -0
- package/dist/lib/helpers/toFormData.d.ts +1 -0
- package/dist/lib/helpers/toFormData.d.ts.map +1 -0
- package/dist/lib/helpers/toURLEncodedForm.d.ts +1 -0
- package/dist/lib/helpers/toURLEncodedForm.d.ts.map +1 -0
- package/dist/lib/helpers/trackStream.d.ts +1 -0
- package/dist/lib/helpers/trackStream.d.ts.map +1 -0
- package/dist/lib/helpers/validator.d.ts +1 -0
- package/dist/lib/helpers/validator.d.ts.map +1 -0
- package/dist/lib/platform.d.ts +2 -1
- package/dist/lib/platform.d.ts.map +1 -0
- package/dist/lib/plugins/attempt.d.ts +2 -0
- package/dist/lib/plugins/attempt.d.ts.map +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 +31 -0
- package/dist/lib/plugins/authBearer.d.ts.map +1 -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 +22 -0
- package/dist/lib/plugins/definePlugin.d.ts.map +1 -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 +66 -0
- package/dist/lib/plugins/retry.d.ts.map +1 -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 +32 -0
- package/dist/lib/plugins/timing.d.ts.map +1 -0
- package/dist/lib/plugins/timing.js +53 -0
- package/dist/lib/plugins/timing.js.map +1 -0
- package/dist/lib/types/standard-schema.d.ts +1 -0
- package/dist/lib/types/standard-schema.d.ts.map +1 -0
- package/dist/lib/types.d.ts +125 -34
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/package.json +42 -19
- package/skills/configure-requests/SKILL.md +380 -0
- package/skills/configure-requests/references/axios-differences.md +67 -0
- package/skills/configure-requests/references/plugins.md +127 -0
- package/src/index.ts +57 -0
- package/src/lib/adapters/fetch-helpers.ts +167 -0
- package/src/lib/adapters/fetch.ts +810 -0
- package/src/lib/cancel/CanceledError.ts +23 -0
- package/src/lib/cancel/isCancel.ts +10 -0
- package/src/lib/core/Faxios.ts +513 -0
- package/src/lib/core/FaxiosError.ts +283 -0
- package/src/lib/core/FaxiosHeaders.ts +529 -0
- package/src/lib/core/buildFullPath.ts +51 -0
- package/src/lib/core/define.ts +127 -0
- package/src/lib/core/dispatchRequest.ts +171 -0
- package/src/lib/core/mergeConfig.ts +175 -0
- package/src/lib/core/prepareRequest.ts +155 -0
- package/src/lib/core/route.ts +68 -0
- package/src/lib/core/settle.ts +20 -0
- package/src/lib/core/transformData.ts +23 -0
- package/src/lib/core/validateSchema.ts +53 -0
- package/src/lib/defaults/index.ts +221 -0
- package/src/lib/defaults/transitional.ts +9 -0
- package/src/lib/env/data.ts +1 -0
- package/src/lib/faxios.ts +306 -0
- package/src/lib/helpers/FaxiosURLSearchParams.ts +52 -0
- package/src/lib/helpers/HttpStatusCode.ts +77 -0
- package/src/lib/helpers/buildURL.ts +77 -0
- package/src/lib/helpers/cloneConfig.ts +23 -0
- package/src/lib/helpers/combineURLs.ts +11 -0
- package/src/lib/helpers/composeSignals.ts +74 -0
- package/src/lib/helpers/cookies.ts +68 -0
- package/src/lib/helpers/deprecatedMethod.ts +32 -0
- package/src/lib/helpers/estimateDataURLDecodedBytes.ts +95 -0
- package/src/lib/helpers/formDataToJSON.ts +122 -0
- package/src/lib/helpers/isAbsoluteURL.ts +13 -0
- package/src/lib/helpers/isFaxiosError.ts +19 -0
- package/src/lib/helpers/isURLSameOrigin.ts +16 -0
- package/src/lib/helpers/parseHeaders.ts +76 -0
- package/src/lib/helpers/progressEventReducer.ts +57 -0
- package/src/lib/helpers/sanitizeHeaderValue.ts +61 -0
- package/src/lib/helpers/speedometer.ts +55 -0
- package/src/lib/helpers/substitutePathParams.ts +13 -0
- package/src/lib/helpers/throttle.ts +45 -0
- package/src/lib/helpers/toFormData.ts +296 -0
- package/src/lib/helpers/toURLEncodedForm.ts +14 -0
- package/src/lib/helpers/trackStream.ts +123 -0
- package/src/lib/helpers/validator.ts +114 -0
- package/src/lib/platform.ts +44 -0
- package/src/lib/plugins/attempt.ts +3 -0
- package/src/lib/plugins/authBearer.ts +124 -0
- package/src/lib/plugins/definePlugin.ts +34 -0
- package/src/lib/plugins/retry.ts +245 -0
- package/src/lib/plugins/timing.ts +76 -0
- package/src/lib/types/standard-schema.ts +58 -0
- package/src/lib/types.ts +496 -0
- package/src/lib/utils.ts +932 -0
- 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,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { getFetch } from "../adapters/fetch.js";
|
|
4
|
+
import CanceledError from "../cancel/CanceledError.js";
|
|
5
|
+
import isCancel from "../cancel/isCancel.js";
|
|
6
|
+
import FaxiosError from "../core/FaxiosError.js";
|
|
7
|
+
import FaxiosHeaders from "../core/FaxiosHeaders.js";
|
|
8
|
+
import { validateSchema } from "../core/validateSchema.js";
|
|
9
|
+
import { substitutePathParams } from "../helpers/substitutePathParams.js";
|
|
10
|
+
import type { InternalFaxiosRequestConfig, FaxiosResponse } from "../types.js";
|
|
11
|
+
import utils from "../utils.js";
|
|
12
|
+
import transformData from "./transformData.js";
|
|
13
|
+
|
|
14
|
+
function throwIfCancellationRequested(config: InternalFaxiosRequestConfig): void {
|
|
15
|
+
if (config.signal && config.signal.aborted) {
|
|
16
|
+
throw new CanceledError(undefined, config);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function validateAndSubstitutePathParams(config: InternalFaxiosRequestConfig): Promise<void> {
|
|
21
|
+
if (config.pathParamsSchema) {
|
|
22
|
+
const validated = await validateSchema(
|
|
23
|
+
config.pathParamsSchema, config.pathParams,
|
|
24
|
+
FaxiosError.ERR_BAD_PATH_PARAMS_SCHEMA, config
|
|
25
|
+
);
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard: schema can return { value: null }
|
|
27
|
+
if (validated == null) {
|
|
28
|
+
throw new FaxiosError(
|
|
29
|
+
"pathParamsSchema returned null/undefined",
|
|
30
|
+
FaxiosError.ERR_BAD_PATH_PARAMS_SCHEMA, config
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, sonarjs/different-types-comparison -- runtime guard: schema may transform to non-Record
|
|
34
|
+
if (typeof validated !== "object" || validated === null || Array.isArray(validated)) {
|
|
35
|
+
throw new FaxiosError(
|
|
36
|
+
"pathParamsSchema must return a plain object",
|
|
37
|
+
FaxiosError.ERR_BAD_PATH_PARAMS_SCHEMA, config
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
config.pathParams = validated;
|
|
41
|
+
}
|
|
42
|
+
const params = config.pathParams ?? {};
|
|
43
|
+
try {
|
|
44
|
+
config.url = substitutePathParams(config.url ?? "", params);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
throw FaxiosError.from(
|
|
48
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
49
|
+
FaxiosError.ERR_BAD_OPTION_VALUE, config
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Intentional mutation: config is the internal merged copy (post-mergeConfig), not the user's original.
|
|
55
|
+
// Validation order (pathParams → params → data) is a fail-fast guarantee — do not reorder.
|
|
56
|
+
// DO NOT make this function async. Returning undefined (not a Promise) on the fast path is a
|
|
57
|
+
// microtask-ordering contract: dispatchRequest must reach the adapter in the same microtask so
|
|
58
|
+
// that a synchronous controller.abort() after request creation is processed by the adapter's
|
|
59
|
+
// signal listener, not before it registers. An async function always returns a Promise, inserting
|
|
60
|
+
// a microtask boundary that breaks abort timing in fetch adapter tests and real-world usage.
|
|
61
|
+
function validatePreFlight(config: InternalFaxiosRequestConfig): Promise<void> | undefined {
|
|
62
|
+
if (config.pathParamsSchema && config.pathParams === undefined) {
|
|
63
|
+
throw new FaxiosError(
|
|
64
|
+
"pathParams is required when pathParamsSchema is configured",
|
|
65
|
+
FaxiosError.ERR_BAD_OPTION_VALUE, config
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const hasSchemas = config.pathParamsSchema || config.paramsSchema || config.requestSchema;
|
|
70
|
+
if (!hasSchemas && config.pathParams === undefined) return undefined;
|
|
71
|
+
|
|
72
|
+
// pathParams present but no schemas — substitute synchronously, no microtask boundary needed.
|
|
73
|
+
if (!hasSchemas) {
|
|
74
|
+
const params = config.pathParams ?? {};
|
|
75
|
+
try { config.url = substitutePathParams(config.url ?? "", params); }
|
|
76
|
+
catch (err) { throw FaxiosError.from(err instanceof Error ? err : new Error(String(err)), FaxiosError.ERR_BAD_OPTION_VALUE, config); }
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (async () => {
|
|
81
|
+
if (config.pathParams !== undefined) {
|
|
82
|
+
await validateAndSubstitutePathParams(config);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (config.paramsSchema) {
|
|
86
|
+
throwIfCancellationRequested(config);
|
|
87
|
+
config.params = await validateSchema(config.paramsSchema, config.params, FaxiosError.ERR_BAD_PARAMS_SCHEMA, config) as typeof config.params;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (config.requestSchema) {
|
|
91
|
+
throwIfCancellationRequested(config);
|
|
92
|
+
config.data = await validateSchema(config.requestSchema, config.data, FaxiosError.ERR_BAD_REQUEST_SCHEMA, config);
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default async function dispatchRequest(this: unknown, config: InternalFaxiosRequestConfig): Promise<FaxiosResponse> {
|
|
98
|
+
throwIfCancellationRequested(config);
|
|
99
|
+
|
|
100
|
+
const preFlight = validatePreFlight(config);
|
|
101
|
+
if (preFlight) await preFlight;
|
|
102
|
+
|
|
103
|
+
config.headers = FaxiosHeaders.from(config.headers) as unknown as typeof config.headers;
|
|
104
|
+
|
|
105
|
+
config.data = transformData.call(config, config.transformRequest);
|
|
106
|
+
|
|
107
|
+
if (config.data != null && !utils.isFormData(config.data) && [ "post", "put", "patch" ].indexOf(config.method as string) !== -1) {
|
|
108
|
+
(config.headers as unknown as { setContentType: (v: string, r: boolean) => void; }).setContentType("application/x-www-form-urlencoded", false);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Last cancellation check: nothing between here and the adapter call may await. An abort that
|
|
112
|
+
// lands during pre-flight validation must not reach the adapter, which only listens for future
|
|
113
|
+
// abort events.
|
|
114
|
+
throwIfCancellationRequested(config);
|
|
115
|
+
|
|
116
|
+
const _adapter = getFetch(config);
|
|
117
|
+
if (!_adapter) {
|
|
118
|
+
throw new FaxiosError(
|
|
119
|
+
"Fetch API is not supported in this environment",
|
|
120
|
+
FaxiosError.ERR_NOT_SUPPORT,
|
|
121
|
+
config
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
const adapter = _adapter as (config: InternalFaxiosRequestConfig) => Promise<FaxiosResponse>;
|
|
125
|
+
|
|
126
|
+
/* eslint-disable promise/always-return */
|
|
127
|
+
return (adapter(config))
|
|
128
|
+
.then(
|
|
129
|
+
|
|
130
|
+
function onAdapterResolution(response: FaxiosResponse) {
|
|
131
|
+
throwIfCancellationRequested(config);
|
|
132
|
+
|
|
133
|
+
(config as unknown as Record<string, unknown>)["response"] = response;
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
response.data = transformData.call(config, config.transformResponse, response);
|
|
137
|
+
response.headers = FaxiosHeaders.from(response.headers);
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
delete (config as unknown as Record<string, unknown>)["response"];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (config.responseSchema) {
|
|
144
|
+
return validateSchema(config.responseSchema, response.data, FaxiosError.ERR_BAD_RESPONSE_SCHEMA, config, response)
|
|
145
|
+
.then(data => { response.data = data; throwIfCancellationRequested(config); return response; });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return response;
|
|
149
|
+
},
|
|
150
|
+
function onAdapterRejection(reason: unknown) {
|
|
151
|
+
if (!isCancel(reason)) {
|
|
152
|
+
throwIfCancellationRequested(config);
|
|
153
|
+
|
|
154
|
+
const r = reason as { response?: { data?: unknown; headers?: unknown; status?: number; [k: string]: unknown; }; } | null;
|
|
155
|
+
if (r?.response) {
|
|
156
|
+
(config as unknown as Record<string, unknown>)["response"] = r.response;
|
|
157
|
+
try {
|
|
158
|
+
r.response.data = transformData.call(config, config.transformResponse, r.response);
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
delete (config as unknown as Record<string, unknown>)["response"];
|
|
162
|
+
}
|
|
163
|
+
r.response.headers = FaxiosHeaders.from(r.response.headers as Record<string, unknown>);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
throw reason;
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
/* eslint-enable promise/always-return */
|
|
171
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import type { FaxiosRequestConfig, FaxiosDefaults } from "../types.js";
|
|
4
|
+
import utils from "../utils.js";
|
|
5
|
+
import FaxiosHeaders from "./FaxiosHeaders.js";
|
|
6
|
+
|
|
7
|
+
const headersToObject = (thing: unknown) => (thing instanceof FaxiosHeaders ? { ...thing } : thing);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Config-specific merge-function which creates a new config-object
|
|
11
|
+
* by merging two configuration objects together.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} config1
|
|
14
|
+
* @param {Object} config2
|
|
15
|
+
*
|
|
16
|
+
* @returns {Object} New object resulting from merging config2 to config1
|
|
17
|
+
*/
|
|
18
|
+
export default function mergeConfig(config1: FaxiosRequestConfig | FaxiosDefaults | Record<string, unknown>, config2?: FaxiosRequestConfig | FaxiosDefaults | Record<string, unknown>): FaxiosRequestConfig & Record<string, unknown> {
|
|
19
|
+
|
|
20
|
+
config2 = config2 || {};
|
|
21
|
+
|
|
22
|
+
// Use a null-prototype object so that downstream reads such as `config.auth`
|
|
23
|
+
// or `config.baseURL` cannot inherit polluted values from Object.prototype.
|
|
24
|
+
// `hasOwnProperty` is restored as a non-enumerable own slot to preserve
|
|
25
|
+
// ergonomics for user code that relies on it.
|
|
26
|
+
const config = Object.create(null);
|
|
27
|
+
Object.defineProperty(config, "hasOwnProperty",
|
|
28
|
+
// Null-proto descriptor so a polluted Object.prototype.get cannot turn
|
|
29
|
+
// this data descriptor into an accessor descriptor on the way in.
|
|
30
|
+
Object.assign(Object.create(null) as PropertyDescriptor, {
|
|
31
|
+
value: Object.prototype.hasOwnProperty,
|
|
32
|
+
enumerable: false,
|
|
33
|
+
writable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
function getMergedValue(target: unknown, source: unknown, _prop?: unknown, caseless?: unknown): unknown {
|
|
39
|
+
if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
|
|
40
|
+
return utils.merge.call({ caseless }, target, source);
|
|
41
|
+
}
|
|
42
|
+
else if (utils.isPlainObject(source)) {
|
|
43
|
+
return utils.merge({}, source);
|
|
44
|
+
}
|
|
45
|
+
else if (utils.isArray(source)) {
|
|
46
|
+
return source.slice();
|
|
47
|
+
}
|
|
48
|
+
return source;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function mergeDeepProperties(a: unknown, b: unknown, prop?: unknown, caseless?: unknown): unknown {
|
|
52
|
+
if (!utils.isUndefined(b)) {
|
|
53
|
+
return getMergedValue(a, b, prop, caseless);
|
|
54
|
+
}
|
|
55
|
+
else if (!utils.isUndefined(a)) {
|
|
56
|
+
return getMergedValue(undefined, a, prop, caseless);
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function valueFromConfig2(_a: unknown, b: unknown): unknown {
|
|
62
|
+
if (!utils.isUndefined(b)) {
|
|
63
|
+
return getMergedValue(undefined, b);
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function defaultToConfig2(a: unknown, b: unknown, prop?: unknown): unknown {
|
|
69
|
+
if (!utils.isUndefined(b)) {
|
|
70
|
+
return getMergedValue(undefined, b);
|
|
71
|
+
}
|
|
72
|
+
// ponytail: explicit undefined in config2 (e.g. xsrfCookieName: undefined) must win over config1's default
|
|
73
|
+
else if (utils.hasOwnProp(config2, prop as string)) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
else if (!utils.isUndefined(a)) {
|
|
77
|
+
return getMergedValue(undefined, a);
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getMergedTransitionalOption(prop: string): unknown {
|
|
83
|
+
const c2 = config2 as Record<string, unknown>;
|
|
84
|
+
const c1 = config1 as Record<string, unknown>;
|
|
85
|
+
const transitional2 = utils.hasOwnProp(config2, "transitional") ? c2["transitional"] : undefined;
|
|
86
|
+
|
|
87
|
+
if (!utils.isUndefined(transitional2)) {
|
|
88
|
+
if (utils.isPlainObject(transitional2)) {
|
|
89
|
+
if (utils.hasOwnProp(transitional2, prop)) {
|
|
90
|
+
return (transitional2 as Record<string, unknown>)[prop];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const transitional1 = utils.hasOwnProp(config1, "transitional") ? c1["transitional"] : undefined;
|
|
99
|
+
|
|
100
|
+
if (utils.isPlainObject(transitional1) && utils.hasOwnProp(transitional1, prop)) {
|
|
101
|
+
return (transitional1 as Record<string, unknown>)[prop];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function mergeDirectKeys(a: unknown, b: unknown, prop?: unknown): unknown {
|
|
108
|
+
if (utils.hasOwnProp(config2, prop as string)) {
|
|
109
|
+
return getMergedValue(a, b);
|
|
110
|
+
}
|
|
111
|
+
else if (utils.hasOwnProp(config1, prop as string)) {
|
|
112
|
+
return getMergedValue(undefined, a);
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const mergeMap = {
|
|
118
|
+
url: valueFromConfig2,
|
|
119
|
+
method: valueFromConfig2,
|
|
120
|
+
data: valueFromConfig2,
|
|
121
|
+
baseURL: defaultToConfig2,
|
|
122
|
+
transformRequest: defaultToConfig2,
|
|
123
|
+
transformResponse: defaultToConfig2,
|
|
124
|
+
paramsSerializer: defaultToConfig2,
|
|
125
|
+
timeout: defaultToConfig2,
|
|
126
|
+
timeoutMessage: defaultToConfig2,
|
|
127
|
+
withCredentials: defaultToConfig2,
|
|
128
|
+
withXSRFToken: defaultToConfig2,
|
|
129
|
+
responseType: defaultToConfig2,
|
|
130
|
+
xsrfCookieName: defaultToConfig2,
|
|
131
|
+
xsrfHeaderName: defaultToConfig2,
|
|
132
|
+
onUploadProgress: defaultToConfig2,
|
|
133
|
+
onDownloadProgress: defaultToConfig2,
|
|
134
|
+
maxContentLength: defaultToConfig2,
|
|
135
|
+
maxBodyLength: defaultToConfig2,
|
|
136
|
+
responseEncoding: defaultToConfig2,
|
|
137
|
+
responseSchema: defaultToConfig2,
|
|
138
|
+
requestSchema: defaultToConfig2,
|
|
139
|
+
paramsSchema: defaultToConfig2,
|
|
140
|
+
pathParams: defaultToConfig2,
|
|
141
|
+
pathParamsSchema: defaultToConfig2,
|
|
142
|
+
validateStatus: mergeDirectKeys,
|
|
143
|
+
headers: (a: unknown, b: unknown, prop?: unknown) =>
|
|
144
|
+
mergeDeepProperties(headersToObject(a), headersToObject(b), prop, true),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const c1 = config1 as Record<string, unknown>;
|
|
148
|
+
const c2 = config2 as Record<string, unknown>;
|
|
149
|
+
const out = config as Record<string, unknown>;
|
|
150
|
+
|
|
151
|
+
utils.forEach(Object.keys({ ...config1, ...config2 }), function computeConfigValue(_value: unknown, _prop: unknown) {
|
|
152
|
+
const key = _value as string;
|
|
153
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") return;
|
|
154
|
+
const mergeFn = (utils.hasOwnProp(mergeMap, key) ? (mergeMap as Record<string, typeof mergeDeepProperties>)[key] : mergeDeepProperties)!;
|
|
155
|
+
const a = utils.hasOwnProp(config1, key) ? c1[key] : undefined;
|
|
156
|
+
const b = utils.hasOwnProp(config2, key) ? c2[key] : undefined;
|
|
157
|
+
const configValue = mergeFn(a, b, key);
|
|
158
|
+
(utils.isUndefined(configValue) && mergeFn !== mergeDirectKeys) || (out[key] = configValue);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
utils.hasOwnProp(config2, "validateStatus") &&
|
|
163
|
+
utils.isUndefined((config2).validateStatus) &&
|
|
164
|
+
getMergedTransitionalOption("validateStatusUndefinedResolves") === false
|
|
165
|
+
) {
|
|
166
|
+
if (utils.hasOwnProp(config1, "validateStatus")) {
|
|
167
|
+
out["validateStatus"] = getMergedValue(undefined, (config1).validateStatus);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
delete out["validateStatus"];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return config;
|
|
175
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import buildFullPath from "../core/buildFullPath.js";
|
|
2
|
+
import FaxiosHeaders from "../core/FaxiosHeaders.js";
|
|
3
|
+
import buildURL from "../helpers/buildURL.js";
|
|
4
|
+
import cloneConfig from "../helpers/cloneConfig.js";
|
|
5
|
+
import cookies from "../helpers/cookies.js";
|
|
6
|
+
import isURLSameOrigin from "../helpers/isURLSameOrigin.js";
|
|
7
|
+
import platform from "../platform.js";
|
|
8
|
+
import type { FaxiosRequestConfig } from "../types.js";
|
|
9
|
+
import utils from "../utils.js";
|
|
10
|
+
|
|
11
|
+
const FORM_DATA_CONTENT_HEADERS = [ "content-type", "content-length" ];
|
|
12
|
+
|
|
13
|
+
function setFormDataHeaders(
|
|
14
|
+
headers: FaxiosHeaders,
|
|
15
|
+
formHeaders: Record<string, unknown>,
|
|
16
|
+
policy: unknown
|
|
17
|
+
): void {
|
|
18
|
+
if (policy !== "content-only") {
|
|
19
|
+
headers.set(formHeaders);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
/* eslint-disable big-o/no-array-lookup-in-loop -- 2-element constant array */
|
|
23
|
+
Object.entries(formHeaders).forEach(([ key, val ]) => {
|
|
24
|
+
if (FORM_DATA_CONTENT_HEADERS.includes(key.toLowerCase())) {
|
|
25
|
+
headers.set(key, val);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
/* eslint-enable big-o/no-array-lookup-in-loop */
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Encode a UTF-8 string to a Latin-1 byte string for use with btoa().
|
|
33
|
+
* This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} str The string to encode
|
|
36
|
+
*
|
|
37
|
+
* @returns {string} UTF-8 bytes as a Latin-1 string
|
|
38
|
+
*/
|
|
39
|
+
const encodeUTF8 = (str: string): string =>
|
|
40
|
+
encodeURIComponent(str).replace(
|
|
41
|
+
/%([0-9A-F]{2})/gi,
|
|
42
|
+
(_: string, hex: string) => String.fromCharCode(parseInt(hex, 16))
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
function applyXSRFToken(
|
|
46
|
+
headers: FaxiosHeaders,
|
|
47
|
+
withXSRFToken: ((cfg: unknown) => unknown) | boolean | null | undefined,
|
|
48
|
+
xsrfHeaderName: string | undefined,
|
|
49
|
+
xsrfCookieName: string | undefined,
|
|
50
|
+
config: FaxiosRequestConfig
|
|
51
|
+
): void {
|
|
52
|
+
if (utils.isFunction(withXSRFToken)) {
|
|
53
|
+
withXSRFToken = (withXSRFToken as (cfg: unknown) => unknown)(
|
|
54
|
+
config
|
|
55
|
+
) as boolean | null | undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Strict boolean check — prevents proto-pollution gadgets (e.g. Object.prototype.withXSRFToken = 1)
|
|
59
|
+
// and misconfigurations (e.g. "false") from short-circuiting the same-origin check and leaking
|
|
60
|
+
// the XSRF token cross-origin.
|
|
61
|
+
const shouldSendXSRF =
|
|
62
|
+
withXSRFToken === true ||
|
|
63
|
+
(withXSRFToken == null && isURLSameOrigin(config.url ?? ""));
|
|
64
|
+
|
|
65
|
+
if (shouldSendXSRF) {
|
|
66
|
+
const xsrfValue =
|
|
67
|
+
xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
|
|
68
|
+
|
|
69
|
+
if (xsrfValue) {
|
|
70
|
+
headers.set(xsrfHeaderName, xsrfValue);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function prepareRequest(config: FaxiosRequestConfig): FaxiosRequestConfig {
|
|
76
|
+
const newConfig = cloneConfig(config);
|
|
77
|
+
|
|
78
|
+
// Read only own properties to prevent prototype pollution gadgets
|
|
79
|
+
// (e.g. Object.prototype.baseURL = 'https://evil.com').
|
|
80
|
+
const own = (key: string) =>
|
|
81
|
+
utils.hasOwnProp(newConfig, key)
|
|
82
|
+
? (newConfig as Record<string, unknown>)[key]
|
|
83
|
+
: undefined;
|
|
84
|
+
|
|
85
|
+
const data = own("data");
|
|
86
|
+
let withXSRFToken = own("withXSRFToken") as
|
|
87
|
+
| ((cfg: unknown) => unknown)
|
|
88
|
+
| boolean
|
|
89
|
+
| null
|
|
90
|
+
| undefined;
|
|
91
|
+
const xsrfHeaderName = own("xsrfHeaderName") as string | undefined;
|
|
92
|
+
const xsrfCookieName = own("xsrfCookieName") as string | undefined;
|
|
93
|
+
const auth = own("auth");
|
|
94
|
+
const baseURL = own("baseURL");
|
|
95
|
+
const allowAbsoluteUrls = own("allowAbsoluteUrls");
|
|
96
|
+
const url = own("url");
|
|
97
|
+
|
|
98
|
+
const headers: FaxiosHeaders = FaxiosHeaders.from(
|
|
99
|
+
own("headers") as Record<string, unknown> | null | undefined
|
|
100
|
+
);
|
|
101
|
+
newConfig.headers = headers;
|
|
102
|
+
|
|
103
|
+
newConfig.url = buildURL(
|
|
104
|
+
buildFullPath(baseURL, url, allowAbsoluteUrls, newConfig),
|
|
105
|
+
own("params"),
|
|
106
|
+
own("paramsSerializer")
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// HTTP basic authentication
|
|
110
|
+
if (auth) {
|
|
111
|
+
const username = String(utils.getSafeProp(auth, "username") || "");
|
|
112
|
+
const password = String(utils.getSafeProp(auth, "password") || "");
|
|
113
|
+
|
|
114
|
+
headers.set(
|
|
115
|
+
"Authorization",
|
|
116
|
+
"Basic " +
|
|
117
|
+
(
|
|
118
|
+
(globalThis as Record<string, unknown>)["btoa"] as (
|
|
119
|
+
s: string
|
|
120
|
+
) => string
|
|
121
|
+
)(username + ":" + (password ? encodeUTF8(password) : ""))
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (utils.isFormData(data)) {
|
|
126
|
+
if (
|
|
127
|
+
platform.hasStandardBrowserEnv ||
|
|
128
|
+
platform.hasStandardBrowserWebWorkerEnv ||
|
|
129
|
+
utils.isReactNative(data)
|
|
130
|
+
) {
|
|
131
|
+
(headers.setContentType as (v: unknown) => unknown)(undefined); // browser/web worker/RN handles it
|
|
132
|
+
}
|
|
133
|
+
else if (
|
|
134
|
+
utils.isFunction((data as Record<string, unknown>)["getHeaders"])
|
|
135
|
+
) {
|
|
136
|
+
// Node.js FormData (like form-data package)
|
|
137
|
+
setFormDataHeaders(
|
|
138
|
+
headers,
|
|
139
|
+
(data as { getHeaders: () => Record<string, unknown>; }).getHeaders(),
|
|
140
|
+
own("formDataHeaderPolicy")
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Add xsrf header
|
|
146
|
+
// This is only done if running in a standard browser environment.
|
|
147
|
+
// Specifically not if we're in a web worker, or react-native.
|
|
148
|
+
if (platform.hasStandardBrowserEnv) {
|
|
149
|
+
applyXSRFToken(headers, withXSRFToken, xsrfHeaderName, xsrfCookieName, newConfig);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return newConfig;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export default prepareRequest;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// @ts-self-types="./route.d.ts" — required for Deno: maps built .js to adjacent .d.ts in dist/
|
|
2
|
+
|
|
3
|
+
import type { StandardSchemaV1 } from "../types/standard-schema.js";
|
|
4
|
+
import type { BasePerCallConfig, DefineConfig, DefinedEndpoint, FaxiosLike } from "./define.js";
|
|
5
|
+
import { createDefinedEndpoint } from "./define.js";
|
|
6
|
+
|
|
7
|
+
/** Config for `route()`: request defaults shared by every method, plus an optional `pathParamsSchema`. */
|
|
8
|
+
export type RouteConfig<PP extends StandardSchemaV1<unknown, Record<string, unknown>> | undefined = undefined> =
|
|
9
|
+
BasePerCallConfig & {
|
|
10
|
+
/**
|
|
11
|
+
* Standard Schema that validates `pathParams` on every call of every method's endpoint; its
|
|
12
|
+
* output fills the `{key}` placeholders. Makes `pathParams` required per call, and a method's
|
|
13
|
+
* config can't replace it. A failure rejects with `ERR_BAD_PATH_PARAMS_SCHEMA`.
|
|
14
|
+
*/
|
|
15
|
+
pathParamsSchema?: PP;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Config for one method of a route; merged over the route config. */
|
|
19
|
+
export type RouteMethodConfig<
|
|
20
|
+
P extends StandardSchemaV1 | undefined = undefined,
|
|
21
|
+
D extends StandardSchemaV1 | undefined = undefined,
|
|
22
|
+
R extends StandardSchemaV1 | undefined = undefined
|
|
23
|
+
> = BasePerCallConfig & {
|
|
24
|
+
paramsSchema?: P;
|
|
25
|
+
requestSchema?: D;
|
|
26
|
+
responseSchema?: R;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** The object `route()` returns: one endpoint factory per HTTP method (no `query`). */
|
|
30
|
+
export type RouteBuilder<PP extends StandardSchemaV1<unknown, Record<string, unknown>> | undefined, TOpts = unknown> = {
|
|
31
|
+
[M in "get" | "post" | "put" | "patch" | "delete" | "head" | "options"]: <
|
|
32
|
+
P extends StandardSchemaV1 | undefined = undefined,
|
|
33
|
+
D extends StandardSchemaV1 | undefined = undefined,
|
|
34
|
+
R extends StandardSchemaV1 | undefined = undefined
|
|
35
|
+
>(config?: RouteMethodConfig<P, D, R> & TOpts) => DefinedEndpoint<PP, P, D, R, TOpts>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// ponytail: `query` excluded — non-standard HTTP method alias, not useful in route definitions
|
|
39
|
+
const methods = [ "get", "post", "put", "patch", "delete", "head", "options" ] as const satisfies ReadonlyArray<keyof RouteBuilder<undefined>>;
|
|
40
|
+
|
|
41
|
+
export function createRouteBuilder<PP extends StandardSchemaV1<unknown, Record<string, unknown>> | undefined>(
|
|
42
|
+
instance: FaxiosLike,
|
|
43
|
+
url: string,
|
|
44
|
+
routeConfig?: RouteConfig<PP>
|
|
45
|
+
): RouteBuilder<PP> {
|
|
46
|
+
const raw = Object.assign(Object.create(null), routeConfig ?? {});
|
|
47
|
+
delete raw.__proto__;
|
|
48
|
+
delete raw.constructor;
|
|
49
|
+
delete raw.prototype;
|
|
50
|
+
const { pathParamsSchema, ...routeDefaults } = raw;
|
|
51
|
+
|
|
52
|
+
const builder = {} as RouteBuilder<PP>;
|
|
53
|
+
for (const method of methods) {
|
|
54
|
+
(builder as Record<string, unknown>)[method] = <
|
|
55
|
+
P extends StandardSchemaV1 | undefined,
|
|
56
|
+
D extends StandardSchemaV1 | undefined,
|
|
57
|
+
R extends StandardSchemaV1 | undefined
|
|
58
|
+
>(methodConfig?: RouteMethodConfig<P, D, R>) => {
|
|
59
|
+
const defineConf = {
|
|
60
|
+
...routeDefaults,
|
|
61
|
+
...(methodConfig ?? {}),
|
|
62
|
+
...(pathParamsSchema !== undefined ? { pathParamsSchema } : {}),
|
|
63
|
+
} as DefineConfig<PP, P, D, R>;
|
|
64
|
+
return createDefinedEndpoint<PP, P, D, R>(instance, method, url, defineConf);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return builder;
|
|
68
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import type { FaxiosResponse } from "../types.js";
|
|
4
|
+
import FaxiosError from "./FaxiosError.js";
|
|
5
|
+
|
|
6
|
+
export function settle(resolve: (value: FaxiosResponse) => void, reject: (reason: unknown) => void, response: FaxiosResponse): void {
|
|
7
|
+
const validateStatus = response.config.validateStatus;
|
|
8
|
+
if (!response.status || !validateStatus || validateStatus(response.status)) {
|
|
9
|
+
resolve(response);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
reject(new FaxiosError(
|
|
13
|
+
"Request failed with status code " + response.status,
|
|
14
|
+
response.status >= 400 && response.status < 500 ? FaxiosError.ERR_BAD_REQUEST : FaxiosError.ERR_BAD_RESPONSE,
|
|
15
|
+
response.config,
|
|
16
|
+
response.request,
|
|
17
|
+
response
|
|
18
|
+
));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import FaxiosHeaders from "../core/FaxiosHeaders.js";
|
|
4
|
+
import defaults from "../defaults/index.js";
|
|
5
|
+
import type { InternalFaxiosRequestConfig, FaxiosRequestHeaders, FaxiosRequestTransformer, FaxiosResponseTransformer } from "../types.js";
|
|
6
|
+
import utils from "../utils.js";
|
|
7
|
+
|
|
8
|
+
type TransformFn = FaxiosRequestTransformer | FaxiosResponseTransformer;
|
|
9
|
+
|
|
10
|
+
export default function transformData(this: unknown, fns: TransformFn | Array<TransformFn> | undefined, response?: { data?: unknown; headers?: unknown; status?: number; } | null): unknown {
|
|
11
|
+
const config = (this || defaults) as InternalFaxiosRequestConfig;
|
|
12
|
+
const context = response || config;
|
|
13
|
+
const headers = FaxiosHeaders.from(context.headers as Record<string, unknown> | undefined);
|
|
14
|
+
let data: unknown = (context as { data?: unknown; }).data;
|
|
15
|
+
|
|
16
|
+
utils.forEach(fns, function transform(fn: unknown) {
|
|
17
|
+
data = (fn as TransformFn).call(config, data, headers as unknown as FaxiosRequestHeaders, response?.status);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
headers.normalize(false);
|
|
21
|
+
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import isCancel from "../cancel/isCancel.js";
|
|
2
|
+
import type { StandardSchemaV1 } from "../types/standard-schema.js";
|
|
3
|
+
import type { InternalFaxiosRequestConfig, FaxiosResponse } from "../types.js";
|
|
4
|
+
import FaxiosError from "./FaxiosError.js";
|
|
5
|
+
|
|
6
|
+
type SchemaErrorCode =
|
|
7
|
+
| typeof FaxiosError.ERR_BAD_RESPONSE_SCHEMA
|
|
8
|
+
| typeof FaxiosError.ERR_BAD_REQUEST_SCHEMA
|
|
9
|
+
| typeof FaxiosError.ERR_BAD_PARAMS_SCHEMA
|
|
10
|
+
| typeof FaxiosError.ERR_BAD_PATH_PARAMS_SCHEMA;
|
|
11
|
+
|
|
12
|
+
export async function validateSchema<S extends StandardSchemaV1>(
|
|
13
|
+
schema: S,
|
|
14
|
+
value: unknown,
|
|
15
|
+
errorCode: SchemaErrorCode,
|
|
16
|
+
config: InternalFaxiosRequestConfig,
|
|
17
|
+
response?: FaxiosResponse
|
|
18
|
+
): Promise<StandardSchemaV1.InferOutput<S>> {
|
|
19
|
+
let result: StandardSchemaV1.Result<unknown> | undefined;
|
|
20
|
+
try {
|
|
21
|
+
const raw = schema["~standard"].validate(value);
|
|
22
|
+
result = raw instanceof Promise ? await raw : raw;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (isCancel(err)) throw err;
|
|
26
|
+
const cause = err instanceof Error ? err : new Error(String(err), { cause: err });
|
|
27
|
+
const wrapped = FaxiosError.from(
|
|
28
|
+
cause, errorCode, config, undefined, response
|
|
29
|
+
);
|
|
30
|
+
wrapped.issues = [{ message: wrapped.message }];
|
|
31
|
+
throw wrapped;
|
|
32
|
+
}
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
34
|
+
if (!result) {
|
|
35
|
+
const error = new FaxiosError(
|
|
36
|
+
`schema['~standard'].validate() returned a non-Result value`,
|
|
37
|
+
errorCode, config, undefined, response
|
|
38
|
+
);
|
|
39
|
+
error.issues = [];
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
if (result.issues !== undefined) {
|
|
43
|
+
const error = new FaxiosError(
|
|
44
|
+
"Schema validation failed",
|
|
45
|
+
errorCode, config, undefined, response
|
|
46
|
+
);
|
|
47
|
+
// Strip to spec-only fields — runtime libs may attach sensitive data
|
|
48
|
+
error.issues = result.issues.map(({ message, path }) => path ? { message, path } : { message });
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
// Standard Schema spec guarantees Result.value aligns with InferOutput
|
|
52
|
+
return result.value;
|
|
53
|
+
}
|