@gcmdev/faxios 0.2.0 → 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.
Files changed (186) hide show
  1. package/LICENSE +8 -0
  2. package/dist/index.d.ts +4 -3
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/adapters/fetch-helpers.d.ts +2 -1
  7. package/dist/lib/adapters/fetch-helpers.d.ts.map +1 -0
  8. package/dist/lib/adapters/fetch.d.ts +1 -0
  9. package/dist/lib/adapters/fetch.d.ts.map +1 -0
  10. package/dist/lib/adapters/fetch.js +86 -26
  11. package/dist/lib/adapters/fetch.js.map +1 -1
  12. package/dist/lib/cancel/CanceledError.d.ts +2 -1
  13. package/dist/lib/cancel/CanceledError.d.ts.map +1 -0
  14. package/dist/lib/cancel/isCancel.d.ts +1 -0
  15. package/dist/lib/cancel/isCancel.d.ts.map +1 -0
  16. package/dist/lib/core/Faxios.d.ts +4 -3
  17. package/dist/lib/core/Faxios.d.ts.map +1 -0
  18. package/dist/lib/core/Faxios.js +8 -4
  19. package/dist/lib/core/Faxios.js.map +1 -1
  20. package/dist/lib/core/FaxiosError.d.ts +3 -2
  21. package/dist/lib/core/FaxiosError.d.ts.map +1 -0
  22. package/dist/lib/core/FaxiosHeaders.d.ts +9 -8
  23. package/dist/lib/core/FaxiosHeaders.d.ts.map +1 -0
  24. package/dist/lib/core/buildFullPath.d.ts +1 -0
  25. package/dist/lib/core/buildFullPath.d.ts.map +1 -0
  26. package/dist/lib/core/define.d.ts +20 -3
  27. package/dist/lib/core/define.d.ts.map +1 -0
  28. package/dist/lib/core/define.js.map +1 -1
  29. package/dist/lib/core/dispatchRequest.d.ts +2 -1
  30. package/dist/lib/core/dispatchRequest.d.ts.map +1 -0
  31. package/dist/lib/core/mergeConfig.d.ts +3 -2
  32. package/dist/lib/core/mergeConfig.d.ts.map +1 -0
  33. package/dist/lib/core/mergeConfig.js +40 -2
  34. package/dist/lib/core/mergeConfig.js.map +1 -1
  35. package/dist/lib/core/prepareRequest.d.ts +1 -0
  36. package/dist/lib/core/prepareRequest.d.ts.map +1 -0
  37. package/dist/lib/core/route.d.ts +10 -1
  38. package/dist/lib/core/route.d.ts.map +1 -0
  39. package/dist/lib/core/route.js +2 -5
  40. package/dist/lib/core/route.js.map +1 -1
  41. package/dist/lib/core/settle.d.ts +1 -0
  42. package/dist/lib/core/settle.d.ts.map +1 -0
  43. package/dist/lib/core/transformData.d.ts +3 -2
  44. package/dist/lib/core/transformData.d.ts.map +1 -0
  45. package/dist/lib/core/validateSchema.d.ts +3 -2
  46. package/dist/lib/core/validateSchema.d.ts.map +1 -0
  47. package/dist/lib/defaults/index.d.ts +1 -0
  48. package/dist/lib/defaults/index.d.ts.map +1 -0
  49. package/dist/lib/defaults/transitional.d.ts +1 -0
  50. package/dist/lib/defaults/transitional.d.ts.map +1 -0
  51. package/dist/lib/env/data.d.ts +2 -1
  52. package/dist/lib/env/data.d.ts.map +1 -0
  53. package/dist/lib/env/data.js +1 -1
  54. package/dist/lib/faxios.d.ts +59 -38
  55. package/dist/lib/faxios.d.ts.map +1 -0
  56. package/dist/lib/faxios.js.map +1 -1
  57. package/dist/lib/helpers/FaxiosURLSearchParams.d.ts +1 -0
  58. package/dist/lib/helpers/FaxiosURLSearchParams.d.ts.map +1 -0
  59. package/dist/lib/helpers/HttpStatusCode.d.ts +1 -0
  60. package/dist/lib/helpers/HttpStatusCode.d.ts.map +1 -0
  61. package/dist/lib/helpers/buildURL.d.ts +1 -0
  62. package/dist/lib/helpers/buildURL.d.ts.map +1 -0
  63. package/dist/lib/helpers/cloneConfig.d.ts +1 -1
  64. package/dist/lib/helpers/cloneConfig.d.ts.map +1 -0
  65. package/dist/lib/helpers/cloneConfig.js +2 -2
  66. package/dist/lib/helpers/cloneConfig.js.map +1 -1
  67. package/dist/lib/helpers/combineURLs.d.ts +1 -0
  68. package/dist/lib/helpers/combineURLs.d.ts.map +1 -0
  69. package/dist/lib/helpers/composeSignals.d.ts +1 -0
  70. package/dist/lib/helpers/composeSignals.d.ts.map +1 -0
  71. package/dist/lib/helpers/cookies.d.ts +2 -1
  72. package/dist/lib/helpers/cookies.d.ts.map +1 -0
  73. package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts +1 -0
  74. package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts.map +1 -0
  75. package/dist/lib/helpers/formDataToJSON.d.ts +2 -1
  76. package/dist/lib/helpers/formDataToJSON.d.ts.map +1 -0
  77. package/dist/lib/helpers/formDataToJSON.js +1 -1
  78. package/dist/lib/helpers/formDataToJSON.js.map +1 -1
  79. package/dist/lib/helpers/isAbsoluteURL.d.ts +1 -0
  80. package/dist/lib/helpers/isAbsoluteURL.d.ts.map +1 -0
  81. package/dist/lib/helpers/isFaxiosError.d.ts +1 -0
  82. package/dist/lib/helpers/isFaxiosError.d.ts.map +1 -0
  83. package/dist/lib/helpers/isURLSameOrigin.d.ts +1 -0
  84. package/dist/lib/helpers/isURLSameOrigin.d.ts.map +1 -0
  85. package/dist/lib/helpers/parseHeaders.d.ts +2 -1
  86. package/dist/lib/helpers/parseHeaders.d.ts.map +1 -0
  87. package/dist/lib/helpers/progressEventReducer.d.ts +1 -0
  88. package/dist/lib/helpers/progressEventReducer.d.ts.map +1 -0
  89. package/dist/lib/helpers/sanitizeHeaderValue.d.ts +1 -0
  90. package/dist/lib/helpers/sanitizeHeaderValue.d.ts.map +1 -0
  91. package/dist/lib/helpers/speedometer.d.ts +1 -0
  92. package/dist/lib/helpers/speedometer.d.ts.map +1 -0
  93. package/dist/lib/helpers/substitutePathParams.d.ts +1 -0
  94. package/dist/lib/helpers/substitutePathParams.d.ts.map +1 -0
  95. package/dist/lib/helpers/substitutePathParams.js +11 -3
  96. package/dist/lib/helpers/substitutePathParams.js.map +1 -1
  97. package/dist/lib/helpers/throttle.d.ts +1 -0
  98. package/dist/lib/helpers/throttle.d.ts.map +1 -0
  99. package/dist/lib/helpers/toFormData.d.ts +1 -0
  100. package/dist/lib/helpers/toFormData.d.ts.map +1 -0
  101. package/dist/lib/helpers/toFormData.js +4 -2
  102. package/dist/lib/helpers/toFormData.js.map +1 -1
  103. package/dist/lib/helpers/toURLEncodedForm.d.ts +1 -0
  104. package/dist/lib/helpers/toURLEncodedForm.d.ts.map +1 -0
  105. package/dist/lib/helpers/trackStream.d.ts +1 -0
  106. package/dist/lib/helpers/trackStream.d.ts.map +1 -0
  107. package/dist/lib/helpers/validator.d.ts +2 -1
  108. package/dist/lib/helpers/validator.d.ts.map +1 -0
  109. package/dist/lib/platform.d.ts +1 -0
  110. package/dist/lib/platform.d.ts.map +1 -0
  111. package/dist/lib/plugins/attempt.d.ts +1 -0
  112. package/dist/lib/plugins/attempt.d.ts.map +1 -0
  113. package/dist/lib/plugins/authBearer.d.ts +10 -5
  114. package/dist/lib/plugins/authBearer.d.ts.map +1 -0
  115. package/dist/lib/plugins/authBearer.js +6 -0
  116. package/dist/lib/plugins/authBearer.js.map +1 -1
  117. package/dist/lib/plugins/definePlugin.d.ts +4 -3
  118. package/dist/lib/plugins/definePlugin.d.ts.map +1 -0
  119. package/dist/lib/plugins/retry.d.ts +24 -23
  120. package/dist/lib/plugins/retry.d.ts.map +1 -0
  121. package/dist/lib/plugins/timing.d.ts +9 -8
  122. package/dist/lib/plugins/timing.d.ts.map +1 -0
  123. package/dist/lib/types/standard-schema.d.ts +7 -6
  124. package/dist/lib/types/standard-schema.d.ts.map +1 -0
  125. package/dist/lib/types.d.ts +104 -92
  126. package/dist/lib/types.d.ts.map +1 -0
  127. package/dist/lib/utils.d.ts +5 -3
  128. package/dist/lib/utils.d.ts.map +1 -0
  129. package/dist/lib/utils.js +9 -2
  130. package/dist/lib/utils.js.map +1 -1
  131. package/package.json +8 -4
  132. package/skills/configure-requests/SKILL.md +53 -8
  133. package/skills/configure-requests/references/plugins.md +54 -6
  134. package/src/index.ts +57 -0
  135. package/src/lib/adapters/fetch-helpers.ts +167 -0
  136. package/src/lib/adapters/fetch.ts +871 -0
  137. package/src/lib/cancel/CanceledError.ts +23 -0
  138. package/src/lib/cancel/isCancel.ts +10 -0
  139. package/src/lib/core/Faxios.ts +517 -0
  140. package/src/lib/core/FaxiosError.ts +283 -0
  141. package/src/lib/core/FaxiosHeaders.ts +529 -0
  142. package/src/lib/core/buildFullPath.ts +51 -0
  143. package/src/lib/core/define.ts +127 -0
  144. package/src/lib/core/dispatchRequest.ts +171 -0
  145. package/src/lib/core/mergeConfig.ts +213 -0
  146. package/src/lib/core/prepareRequest.ts +155 -0
  147. package/src/lib/core/route.ts +65 -0
  148. package/src/lib/core/settle.ts +20 -0
  149. package/src/lib/core/transformData.ts +23 -0
  150. package/src/lib/core/validateSchema.ts +53 -0
  151. package/src/lib/defaults/index.ts +221 -0
  152. package/src/lib/defaults/transitional.ts +9 -0
  153. package/src/lib/env/data.ts +1 -0
  154. package/src/lib/faxios.ts +306 -0
  155. package/src/lib/helpers/FaxiosURLSearchParams.ts +52 -0
  156. package/src/lib/helpers/HttpStatusCode.ts +77 -0
  157. package/src/lib/helpers/buildURL.ts +77 -0
  158. package/src/lib/helpers/cloneConfig.ts +22 -0
  159. package/src/lib/helpers/combineURLs.ts +11 -0
  160. package/src/lib/helpers/composeSignals.ts +74 -0
  161. package/src/lib/helpers/cookies.ts +68 -0
  162. package/src/lib/helpers/deprecatedMethod.ts +32 -0
  163. package/src/lib/helpers/estimateDataURLDecodedBytes.ts +95 -0
  164. package/src/lib/helpers/formDataToJSON.ts +122 -0
  165. package/src/lib/helpers/isAbsoluteURL.ts +13 -0
  166. package/src/lib/helpers/isFaxiosError.ts +19 -0
  167. package/src/lib/helpers/isURLSameOrigin.ts +16 -0
  168. package/src/lib/helpers/parseHeaders.ts +76 -0
  169. package/src/lib/helpers/progressEventReducer.ts +57 -0
  170. package/src/lib/helpers/sanitizeHeaderValue.ts +61 -0
  171. package/src/lib/helpers/speedometer.ts +55 -0
  172. package/src/lib/helpers/substitutePathParams.ts +23 -0
  173. package/src/lib/helpers/throttle.ts +45 -0
  174. package/src/lib/helpers/toFormData.ts +298 -0
  175. package/src/lib/helpers/toURLEncodedForm.ts +14 -0
  176. package/src/lib/helpers/trackStream.ts +123 -0
  177. package/src/lib/helpers/validator.ts +114 -0
  178. package/src/lib/platform.ts +44 -0
  179. package/src/lib/plugins/attempt.ts +3 -0
  180. package/src/lib/plugins/authBearer.ts +129 -0
  181. package/src/lib/plugins/definePlugin.ts +34 -0
  182. package/src/lib/plugins/retry.ts +245 -0
  183. package/src/lib/plugins/timing.ts +76 -0
  184. package/src/lib/types/standard-schema.ts +58 -0
  185. package/src/lib/types.ts +496 -0
  186. package/src/lib/utils.ts +941 -0
@@ -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 { FaxiosResponse, InternalFaxiosRequestConfig } 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 null | { response?: { [k: string]: unknown; data?: unknown; headers?: unknown; status?: number; }; };
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,213 @@
1
+ "use strict";
2
+
3
+ import type { FaxiosDefaults, FaxiosRequestConfig } 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: FaxiosDefaults | FaxiosRequestConfig | Record<string, unknown>, config2?: FaxiosDefaults | FaxiosRequestConfig | 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
+ // Shallow own-key copy into a null-prototype object, later sources winning. Every own key is
83
+ // kept, including `constructor`, `prototype` and `__proto__`: `params` are caller data that
84
+ // reach the query string, and defineProperty stores them as plain data slots.
85
+ function ownCopy(...sources: Array<unknown>): Record<string, unknown> {
86
+ const out: Record<string, unknown> = Object.create(null);
87
+ for (const source of sources) {
88
+ if (!utils.isPlainObject(source)) continue;
89
+ const record = source as Record<string, unknown>;
90
+ for (const key of Object.keys(record)) {
91
+ Object.defineProperty(out, key, Object.assign(Object.create(null) as PropertyDescriptor, {
92
+ value: record[key],
93
+ enumerable: true,
94
+ writable: true,
95
+ configurable: true,
96
+ }));
97
+ }
98
+ }
99
+ return out;
100
+ }
101
+
102
+ // `params`: a plain object is copied with every own key; anything else (URLSearchParams,
103
+ // a custom serializer's input) passes through as given.
104
+ function mergeParams(a: unknown, b: unknown): unknown {
105
+ const source = utils.isUndefined(b) ? a : b;
106
+ if (!utils.isPlainObject(source)) return source;
107
+ return utils.isUndefined(b) ? ownCopy(a) : ownCopy(a, b);
108
+ }
109
+
110
+ // `env`: shallow, null-prototype, so `fetch`, `Request` and `Response` can only be own values.
111
+ function mergeEnv(a: unknown, b: unknown): unknown {
112
+ if (utils.isUndefined(a) && utils.isUndefined(b)) return undefined;
113
+ return ownCopy(a, b);
114
+ }
115
+
116
+ function getMergedTransitionalOption(prop: string): unknown {
117
+ const c2 = config2 as Record<string, unknown>;
118
+ const c1 = config1 as Record<string, unknown>;
119
+ const transitional2 = utils.hasOwnProp(config2, "transitional") ? c2["transitional"] : undefined;
120
+
121
+ if (!utils.isUndefined(transitional2)) {
122
+ if (utils.isPlainObject(transitional2)) {
123
+ if (utils.hasOwnProp(transitional2, prop)) {
124
+ return (transitional2 as Record<string, unknown>)[prop];
125
+ }
126
+ }
127
+ else {
128
+ return undefined;
129
+ }
130
+ }
131
+
132
+ const transitional1 = utils.hasOwnProp(config1, "transitional") ? c1["transitional"] : undefined;
133
+
134
+ if (utils.isPlainObject(transitional1) && utils.hasOwnProp(transitional1, prop)) {
135
+ return (transitional1 as Record<string, unknown>)[prop];
136
+ }
137
+
138
+ return undefined;
139
+ }
140
+
141
+ function mergeDirectKeys(a: unknown, b: unknown, prop?: unknown): unknown {
142
+ if (utils.hasOwnProp(config2, prop as string)) {
143
+ return getMergedValue(a, b);
144
+ }
145
+ else if (utils.hasOwnProp(config1, prop as string)) {
146
+ return getMergedValue(undefined, a);
147
+ }
148
+ return undefined;
149
+ }
150
+
151
+ const mergeMap = {
152
+ url: valueFromConfig2,
153
+ method: valueFromConfig2,
154
+ // The body is passed by reference: faxios never mutates it, and copying would drop keys such
155
+ // as `constructor` and cost a deep clone per request.
156
+ data: (_a: unknown, b: unknown) => b,
157
+ params: mergeParams,
158
+ env: mergeEnv,
159
+ baseURL: defaultToConfig2,
160
+ transformRequest: defaultToConfig2,
161
+ transformResponse: defaultToConfig2,
162
+ paramsSerializer: defaultToConfig2,
163
+ timeout: defaultToConfig2,
164
+ timeoutMessage: defaultToConfig2,
165
+ withCredentials: defaultToConfig2,
166
+ withXSRFToken: defaultToConfig2,
167
+ responseType: defaultToConfig2,
168
+ xsrfCookieName: defaultToConfig2,
169
+ xsrfHeaderName: defaultToConfig2,
170
+ onUploadProgress: defaultToConfig2,
171
+ onDownloadProgress: defaultToConfig2,
172
+ maxContentLength: defaultToConfig2,
173
+ maxBodyLength: defaultToConfig2,
174
+ responseEncoding: defaultToConfig2,
175
+ responseSchema: defaultToConfig2,
176
+ requestSchema: defaultToConfig2,
177
+ paramsSchema: defaultToConfig2,
178
+ pathParams: defaultToConfig2,
179
+ pathParamsSchema: defaultToConfig2,
180
+ validateStatus: mergeDirectKeys,
181
+ headers: (a: unknown, b: unknown, prop?: unknown) =>
182
+ mergeDeepProperties(headersToObject(a), headersToObject(b), prop, true),
183
+ };
184
+
185
+ const c1 = config1 as Record<string, unknown>;
186
+ const c2 = config2 as Record<string, unknown>;
187
+ const out = config as Record<string, unknown>;
188
+
189
+ utils.forEach(Object.keys({ ...config1, ...config2 }), function computeConfigValue(_value: unknown, _prop: unknown) {
190
+ const key = _value as string;
191
+ if (utils.isDangerousKey(key)) return;
192
+ const mergeFn = (utils.hasOwnProp(mergeMap, key) ? (mergeMap as Record<string, typeof mergeDeepProperties>)[key] : mergeDeepProperties)!;
193
+ const a = utils.hasOwnProp(config1, key) ? c1[key] : undefined;
194
+ const b = utils.hasOwnProp(config2, key) ? c2[key] : undefined;
195
+ const configValue = mergeFn(a, b, key);
196
+ (utils.isUndefined(configValue) && mergeFn !== mergeDirectKeys) || (out[key] = configValue);
197
+ });
198
+
199
+ if (
200
+ utils.hasOwnProp(config2, "validateStatus") &&
201
+ utils.isUndefined((config2).validateStatus) &&
202
+ getMergedTransitionalOption("validateStatusUndefinedResolves") === false
203
+ ) {
204
+ if (utils.hasOwnProp(config1, "validateStatus")) {
205
+ out["validateStatus"] = getMergedValue(undefined, (config1).validateStatus);
206
+ }
207
+ else {
208
+ delete out["validateStatus"];
209
+ }
210
+ }
211
+
212
+ return config;
213
+ }
@@ -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 null | Record<string, unknown> | 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,65 @@
1
+ // @ts-self-types="./route.d.ts" — required for Deno: maps built .js to adjacent .d.ts in dist/
2
+
3
+ import cloneConfig from "../helpers/cloneConfig.js";
4
+ import type { StandardSchemaV1 } from "../types/standard-schema.js";
5
+ import type { BasePerCallConfig, DefineConfig, DefinedEndpoint, FaxiosLike } from "./define.js";
6
+ import { createDefinedEndpoint } from "./define.js";
7
+
8
+ /** Config for `route()`: request defaults shared by every method, plus an optional `pathParamsSchema`. */
9
+ export type RouteConfig<PP extends StandardSchemaV1<unknown, Record<string, unknown>> | undefined = undefined> =
10
+ BasePerCallConfig & {
11
+ /**
12
+ * Standard Schema that validates `pathParams` on every call of every method's endpoint; its
13
+ * output fills the `{key}` placeholders. Makes `pathParams` required per call, and a method's
14
+ * config can't replace it. A failure rejects with `ERR_BAD_PATH_PARAMS_SCHEMA`.
15
+ */
16
+ pathParamsSchema?: PP;
17
+ };
18
+
19
+ /** Config for one method of a route; merged over the route config. */
20
+ export type RouteMethodConfig<
21
+ P extends StandardSchemaV1 | undefined = undefined,
22
+ D extends StandardSchemaV1 | undefined = undefined,
23
+ R extends StandardSchemaV1 | undefined = undefined
24
+ > = BasePerCallConfig & {
25
+ paramsSchema?: P;
26
+ requestSchema?: D;
27
+ responseSchema?: R;
28
+ };
29
+
30
+ /** The object `route()` returns: one endpoint factory per HTTP method (no `query`). */
31
+ export type RouteBuilder<PP extends StandardSchemaV1<unknown, Record<string, unknown>> | undefined, TOpts = unknown> = {
32
+ [M in "delete" | "get" | "head" | "options" | "patch" | "post" | "put"]: <
33
+ P extends StandardSchemaV1 | undefined = undefined,
34
+ D extends StandardSchemaV1 | undefined = undefined,
35
+ R extends StandardSchemaV1 | undefined = undefined
36
+ >(config?: RouteMethodConfig<P, D, R> & TOpts) => DefinedEndpoint<PP, P, D, R, TOpts>;
37
+ };
38
+
39
+ // ponytail: `query` excluded — non-standard HTTP method alias, not useful in route definitions
40
+ const methods = [ "get", "post", "put", "patch", "delete", "head", "options" ] as const satisfies ReadonlyArray<keyof RouteBuilder<undefined>>;
41
+
42
+ export function createRouteBuilder<PP extends StandardSchemaV1<unknown, Record<string, unknown>> | undefined>(
43
+ instance: FaxiosLike,
44
+ url: string,
45
+ routeConfig?: RouteConfig<PP>
46
+ ): RouteBuilder<PP> {
47
+ const { pathParamsSchema, ...routeDefaults } = cloneConfig(routeConfig ?? {});
48
+
49
+ const builder = {} as RouteBuilder<PP>;
50
+ for (const method of methods) {
51
+ (builder as Record<string, unknown>)[method] = <
52
+ P extends StandardSchemaV1 | undefined,
53
+ D extends StandardSchemaV1 | undefined,
54
+ R extends StandardSchemaV1 | undefined
55
+ >(methodConfig?: RouteMethodConfig<P, D, R>) => {
56
+ const defineConf = {
57
+ ...routeDefaults,
58
+ ...(methodConfig ?? {}),
59
+ ...(pathParamsSchema !== undefined ? { pathParamsSchema } : {}),
60
+ } as DefineConfig<PP, P, D, R>;
61
+ return createDefinedEndpoint<PP, P, D, R>(instance, method, url, defineConf);
62
+ };
63
+ }
64
+ return builder;
65
+ }
@@ -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 { FaxiosRequestHeaders, FaxiosRequestTransformer, FaxiosResponseTransformer, InternalFaxiosRequestConfig } from "../types.js";
6
+ import utils from "../utils.js";
7
+
8
+ type TransformFn = FaxiosRequestTransformer | FaxiosResponseTransformer;
9
+
10
+ export default function transformData(this: unknown, fns: Array<TransformFn> | TransformFn | undefined, response?: null | { data?: unknown; headers?: unknown; status?: number; }): 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
+ }