@gcmdev/faxios 0.2.1 → 0.2.3

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 (131) hide show
  1. package/dist/index.d.ts +3 -3
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/adapters/fetch-helpers.d.ts +1 -1
  6. package/dist/lib/adapters/fetch-helpers.d.ts.map +1 -1
  7. package/dist/lib/adapters/fetch.d.ts +4 -1
  8. package/dist/lib/adapters/fetch.d.ts.map +1 -1
  9. package/dist/lib/adapters/fetch.js +210 -92
  10. package/dist/lib/adapters/fetch.js.map +1 -1
  11. package/dist/lib/cancel/CanceledError.d.ts +1 -1
  12. package/dist/lib/cancel/CanceledError.d.ts.map +1 -1
  13. package/dist/lib/core/Faxios.d.ts +3 -3
  14. package/dist/lib/core/Faxios.d.ts.map +1 -1
  15. package/dist/lib/core/Faxios.js +8 -4
  16. package/dist/lib/core/Faxios.js.map +1 -1
  17. package/dist/lib/core/FaxiosError.d.ts +2 -2
  18. package/dist/lib/core/FaxiosError.d.ts.map +1 -1
  19. package/dist/lib/core/FaxiosHeaders.d.ts +8 -8
  20. package/dist/lib/core/FaxiosHeaders.d.ts.map +1 -1
  21. package/dist/lib/core/FaxiosHeaders.js +31 -7
  22. package/dist/lib/core/FaxiosHeaders.js.map +1 -1
  23. package/dist/lib/core/define.d.ts +3 -3
  24. package/dist/lib/core/define.d.ts.map +1 -1
  25. package/dist/lib/core/dispatchRequest.d.ts +1 -1
  26. package/dist/lib/core/dispatchRequest.d.ts.map +1 -1
  27. package/dist/lib/core/dispatchRequest.js +6 -1
  28. package/dist/lib/core/dispatchRequest.js.map +1 -1
  29. package/dist/lib/core/mergeConfig.d.ts +2 -2
  30. package/dist/lib/core/mergeConfig.d.ts.map +1 -1
  31. package/dist/lib/core/mergeConfig.js +40 -2
  32. package/dist/lib/core/mergeConfig.js.map +1 -1
  33. package/dist/lib/core/route.d.ts +1 -1
  34. package/dist/lib/core/route.d.ts.map +1 -1
  35. package/dist/lib/core/route.js +2 -5
  36. package/dist/lib/core/route.js.map +1 -1
  37. package/dist/lib/core/transformData.d.ts +2 -2
  38. package/dist/lib/core/transformData.d.ts.map +1 -1
  39. package/dist/lib/core/validateSchema.d.ts +2 -2
  40. package/dist/lib/core/validateSchema.d.ts.map +1 -1
  41. package/dist/lib/env/data.d.ts +1 -1
  42. package/dist/lib/env/data.js +1 -1
  43. package/dist/lib/faxios.d.ts +50 -50
  44. package/dist/lib/faxios.d.ts.map +1 -1
  45. package/dist/lib/helpers/cloneConfig.d.ts +0 -1
  46. package/dist/lib/helpers/cloneConfig.d.ts.map +1 -1
  47. package/dist/lib/helpers/cloneConfig.js +2 -2
  48. package/dist/lib/helpers/cloneConfig.js.map +1 -1
  49. package/dist/lib/helpers/cookies.d.ts +1 -1
  50. package/dist/lib/helpers/cookies.d.ts.map +1 -1
  51. package/dist/lib/helpers/formDataToJSON.d.ts +1 -1
  52. package/dist/lib/helpers/formDataToJSON.d.ts.map +1 -1
  53. package/dist/lib/helpers/formDataToJSON.js +1 -1
  54. package/dist/lib/helpers/formDataToJSON.js.map +1 -1
  55. package/dist/lib/helpers/parseHeaders.d.ts +1 -1
  56. package/dist/lib/helpers/parseHeaders.d.ts.map +1 -1
  57. package/dist/lib/helpers/progressEventReducer.d.ts.map +1 -1
  58. package/dist/lib/helpers/progressEventReducer.js +14 -1
  59. package/dist/lib/helpers/progressEventReducer.js.map +1 -1
  60. package/dist/lib/helpers/substitutePathParams.d.ts.map +1 -1
  61. package/dist/lib/helpers/substitutePathParams.js +11 -3
  62. package/dist/lib/helpers/substitutePathParams.js.map +1 -1
  63. package/dist/lib/helpers/toFormData.d.ts.map +1 -1
  64. package/dist/lib/helpers/toFormData.js +16 -20
  65. package/dist/lib/helpers/toFormData.js.map +1 -1
  66. package/dist/lib/helpers/trackStream.d.ts.map +1 -1
  67. package/dist/lib/helpers/trackStream.js +27 -3
  68. package/dist/lib/helpers/trackStream.js.map +1 -1
  69. package/dist/lib/helpers/validator.d.ts +1 -1
  70. package/dist/lib/helpers/validator.d.ts.map +1 -1
  71. package/dist/lib/platform.d.ts.map +1 -1
  72. package/dist/lib/plugins/authBearer.d.ts +9 -5
  73. package/dist/lib/plugins/authBearer.d.ts.map +1 -1
  74. package/dist/lib/plugins/authBearer.js +6 -0
  75. package/dist/lib/plugins/authBearer.js.map +1 -1
  76. package/dist/lib/plugins/definePlugin.d.ts +3 -3
  77. package/dist/lib/plugins/definePlugin.d.ts.map +1 -1
  78. package/dist/lib/plugins/retry.d.ts +23 -23
  79. package/dist/lib/plugins/retry.d.ts.map +1 -1
  80. package/dist/lib/plugins/timing.d.ts +8 -8
  81. package/dist/lib/plugins/timing.d.ts.map +1 -1
  82. package/dist/lib/types/standard-schema.d.ts +6 -6
  83. package/dist/lib/types/standard-schema.d.ts.map +1 -1
  84. package/dist/lib/types.d.ts +106 -92
  85. package/dist/lib/types.d.ts.map +1 -1
  86. package/dist/lib/utils.d.ts +4 -3
  87. package/dist/lib/utils.d.ts.map +1 -1
  88. package/dist/lib/utils.js +18 -5
  89. package/dist/lib/utils.js.map +1 -1
  90. package/package.json +7 -4
  91. package/skills/configure-requests/SKILL.md +14 -8
  92. package/skills/configure-requests/references/axios-differences.md +1 -1
  93. package/skills/configure-requests/references/plugins.md +37 -6
  94. package/src/index.ts +15 -15
  95. package/src/lib/adapters/fetch-helpers.ts +3 -3
  96. package/src/lib/adapters/fetch.ts +263 -144
  97. package/src/lib/cancel/CanceledError.ts +1 -1
  98. package/src/lib/core/Faxios.ts +17 -13
  99. package/src/lib/core/FaxiosError.ts +3 -3
  100. package/src/lib/core/FaxiosHeaders.ts +50 -23
  101. package/src/lib/core/define.ts +7 -7
  102. package/src/lib/core/dispatchRequest.ts +8 -3
  103. package/src/lib/core/mergeConfig.ts +42 -4
  104. package/src/lib/core/prepareRequest.ts +1 -1
  105. package/src/lib/core/route.ts +3 -6
  106. package/src/lib/core/transformData.ts +2 -2
  107. package/src/lib/core/validateSchema.ts +4 -4
  108. package/src/lib/defaults/index.ts +1 -1
  109. package/src/lib/env/data.ts +1 -1
  110. package/src/lib/faxios.ts +55 -55
  111. package/src/lib/helpers/HttpStatusCode.ts +1 -1
  112. package/src/lib/helpers/cloneConfig.ts +2 -3
  113. package/src/lib/helpers/composeSignals.ts +4 -4
  114. package/src/lib/helpers/cookies.ts +3 -3
  115. package/src/lib/helpers/formDataToJSON.ts +4 -4
  116. package/src/lib/helpers/parseHeaders.ts +2 -2
  117. package/src/lib/helpers/progressEventReducer.ts +15 -2
  118. package/src/lib/helpers/sanitizeHeaderValue.ts +1 -1
  119. package/src/lib/helpers/substitutePathParams.ts +13 -3
  120. package/src/lib/helpers/throttle.ts +1 -1
  121. package/src/lib/helpers/toFormData.ts +26 -28
  122. package/src/lib/helpers/trackStream.ts +40 -12
  123. package/src/lib/helpers/validator.ts +3 -3
  124. package/src/lib/platform.ts +4 -4
  125. package/src/lib/plugins/authBearer.ts +14 -9
  126. package/src/lib/plugins/definePlugin.ts +3 -3
  127. package/src/lib/plugins/retry.ts +27 -27
  128. package/src/lib/plugins/timing.ts +5 -5
  129. package/src/lib/types/standard-schema.ts +6 -6
  130. package/src/lib/types.ts +139 -125
  131. package/src/lib/utils.ts +30 -15
package/src/lib/faxios.ts CHANGED
@@ -15,8 +15,8 @@ import formDataToJSON from "./helpers/formDataToJSON.js";
15
15
  import HttpStatusCode from "./helpers/HttpStatusCode.js";
16
16
  import isFaxiosError from "./helpers/isFaxiosError.js";
17
17
  import toFormData from "./helpers/toFormData.js";
18
+ import type { CreateFaxiosDefaults, FaxiosDefaults, FaxiosHeaderValue, FaxiosMiddleware, FaxiosPluginArgument, FaxiosRequestConfig, FaxiosResponse, HeadersDefaults, Method, SchemaConfig, StringLiteralsOrString } from "./types.js";
18
19
  import type { StandardSchemaV1 } from "./types/standard-schema.js";
19
- import type { Method, StringLiteralsOrString, CreateFaxiosDefaults, FaxiosDefaults, FaxiosHeaderValue, FaxiosMiddleware, FaxiosPluginArgument, FaxiosRequestConfig, HeadersDefaults, FaxiosResponse, SchemaConfig } from "./types.js";
20
20
  import utils from "./utils.js";
21
21
 
22
22
  /**
@@ -91,11 +91,11 @@ type ProvidedCapabilities<TProv> = [TProv] extends [null | undefined] ? {} : TPr
91
91
  // use() reads capability names with Object.keys() and skips the keys that could pollute a prototype,
92
92
  // so the registry only holds string names. A symbol, number or dangerous key would be typed on
93
93
  // ctx.capabilities without matching a registered name, and nothing could satisfy a requirement for one.
94
- type UnsupportedCapabilityKeys<TCaps> = Extract<keyof TCaps, symbol | number | "__proto__" | "constructor" | "prototype">;
94
+ type UnsupportedCapabilityKeys<TCaps> = Extract<keyof TCaps, "__proto__" | "constructor" | "prototype" | number | symbol>;
95
95
 
96
- type RejectUnsupportedCapabilityKeys<TReq, TProv> = [UnsupportedCapabilityKeys<TReq> | UnsupportedCapabilityKeys<TProv>] extends [never]
96
+ type RejectUnsupportedCapabilityKeys<TReq, TProv> = [UnsupportedCapabilityKeys<TProv> | UnsupportedCapabilityKeys<TReq>] extends [never]
97
97
  ? unknown
98
- : { "faxios: capability names must be strings other than __proto__, constructor and prototype": UnsupportedCapabilityKeys<TReq> | UnsupportedCapabilityKeys<TProv>; };
98
+ : { "faxios: capability names must be strings other than __proto__, constructor and prototype": UnsupportedCapabilityKeys<TProv> | UnsupportedCapabilityKeys<TReq>; };
99
99
 
100
100
  // Nothing sets a plugin option for the caller, so a required one would be typed as present in the
101
101
  // plugin's middleware while every request that omits it leaves it undefined.
@@ -130,23 +130,6 @@ type CheckPlugin<THave, TReq, TProv, TOpt> = RequireCapabilities<THave, TReq>
130
130
  // without narrowing first.
131
131
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- see above
132
132
  export type FaxiosInstance<TOpts = {}, TCaps = {}> = {
133
- /**
134
- * Register middleware or a plugin and return this instance, typed with the plugin's request
135
- * options and capabilities added. A plugin whose required capabilities aren't installed yet, or
136
- * that provides a capability another installed plugin already provides, is a type error.
137
- */
138
- use: <TReq = unknown, TProv = unknown, TOpt = unknown>(
139
- middleware: FaxiosMiddleware<TOpts, TCaps> | (FaxiosPluginArgument<TReq, TProv, TOpt> & CheckPlugin<TCaps, TReq, TProv, TOpt>)
140
- ) => FaxiosInstance<TOpts & TOpt, TCaps & ProvidedCapabilities<TProv>>;
141
- /**
142
- * Remove middleware or a plugin registered with use(), by reference. In-flight requests are
143
- * unaffected. The instance type is not narrowed: request options and capabilities the ejected
144
- * plugin added stay in its type.
145
- */
146
- // A method, so its parameter is checked bivariantly: middleware typed for this instance's
147
- // options and capabilities is accepted.
148
- // eslint-disable-next-line @typescript-eslint/method-signature-style -- see above
149
- eject<TReq = unknown, TProv = unknown, TOpt = unknown>(middleware: FaxiosMiddleware<TOpt> | FaxiosPluginArgument<TReq, TProv, TOpt>): void;
150
133
  /**
151
134
  * Define a typed endpoint for `method` and `url` (with optional `{key}` placeholders). `config`
152
135
  * holds request defaults and the `pathParamsSchema`, `paramsSchema`, `requestSchema` and
@@ -165,6 +148,15 @@ export type FaxiosInstance<TOpts = {}, TCaps = {}> = {
165
148
  url: string,
166
149
  config?: DefineConfig<PP, P, D, R> & TOpts
167
150
  ) => DefinedEndpoint<PP, P, D, R, TOpts>;
151
+ /**
152
+ * Remove middleware or a plugin registered with use(), by reference. In-flight requests are
153
+ * unaffected. The instance type is not narrowed: request options and capabilities the ejected
154
+ * plugin added stay in its type.
155
+ */
156
+ // A method, so its parameter is checked bivariantly: middleware typed for this instance's
157
+ // options and capabilities is accepted.
158
+ // eslint-disable-next-line @typescript-eslint/method-signature-style -- see above
159
+ eject<TReq = unknown, TProv = unknown, TOpt = unknown>(middleware: FaxiosMiddleware<TOpt> | FaxiosPluginArgument<TReq, TProv, TOpt>): void;
168
160
  /**
169
161
  * Group endpoints that share `url` and `config`. Returns one endpoint factory per method (`get`,
170
162
  * `post`, `put`, `patch`, `delete`, `head`, `options`; no `query`), each taking `paramsSchema`,
@@ -176,22 +168,38 @@ export type FaxiosInstance<TOpts = {}, TCaps = {}> = {
176
168
  url: string,
177
169
  config?: RouteConfig<PP> & TOpts
178
170
  ) => RouteBuilder<PP, TOpts>;
171
+ /**
172
+ * Register middleware or a plugin and return this instance, typed with the plugin's request
173
+ * options and capabilities added. A plugin whose required capabilities aren't installed yet, or
174
+ * that provides a capability another installed plugin already provides, is a type error.
175
+ */
176
+ use: <TReq = unknown, TProv = unknown, TOpt = unknown>(
177
+ middleware: FaxiosMiddleware<TOpts, TCaps> | (FaxiosPluginArgument<TReq, TProv, TOpt> & CheckPlugin<TCaps, TReq, TProv, TOpt>)
178
+ ) => FaxiosInstance<TOpts & TOpt, TCaps & ProvidedCapabilities<TProv>>;
179
179
  <O, D = unknown>(config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
180
180
  <O, D = unknown>(url: string, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
181
181
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(config: FaxiosRequestConfig<D> & TOpts): Promise<R>;
182
182
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
183
- request: {
184
- <O, D = unknown>(config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
185
- <T = unknown, R = FaxiosResponse<T>, D = unknown>(config: FaxiosRequestConfig<D> & TOpts): Promise<R>;
186
- };
187
- get: {
183
+ create: (instanceConfig?: CreateFaxiosDefaults) => FaxiosInstance;
184
+ // `common` plus one header group per method; a key set directly on
185
+ // `headers` applies to every method.
186
+ defaults: Omit<FaxiosDefaults, "headers"> & {
187
+ headers: HeadersDefaults & { [key: string]: FaxiosHeaderValue | undefined; };
188
+ } & Partial<TOpts>;
189
+ delete: {
188
190
  <O, D = unknown>(url: string, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
189
191
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
190
192
  };
191
- delete: {
193
+ get: {
192
194
  <O, D = unknown>(url: string, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
193
195
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
194
196
  };
197
+ /**
198
+ * Build the URL for `config` without sending a request. Applies `baseURL`, `params` and
199
+ * `paramsSerializer`, but does not substitute `pathParams` or run schemas, so `{key}`
200
+ * placeholders come back as written.
201
+ */
202
+ getUri: (config?: FaxiosRequestConfig & TOpts) => string;
195
203
  head: {
196
204
  <O, D = unknown>(url: string, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
197
205
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
@@ -200,23 +208,23 @@ export type FaxiosInstance<TOpts = {}, TCaps = {}> = {
200
208
  <O, D = unknown>(url: string, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
201
209
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
202
210
  };
203
- post: {
211
+ patch: {
204
212
  <O, D = unknown>(url: string, data: D | undefined, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
205
213
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, data?: D, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
206
214
  };
207
- put: {
215
+ patchForm: {
208
216
  <O, D = unknown>(url: string, data: D | undefined, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
209
217
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, data?: D, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
210
218
  };
211
- patch: {
219
+ post: {
212
220
  <O, D = unknown>(url: string, data: D | undefined, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
213
221
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, data?: D, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
214
222
  };
215
- query: {
223
+ postForm: {
216
224
  <O, D = unknown>(url: string, data: D | undefined, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
217
225
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, data?: D, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
218
226
  };
219
- postForm: {
227
+ put: {
220
228
  <O, D = unknown>(url: string, data: D | undefined, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
221
229
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, data?: D, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
222
230
  };
@@ -224,41 +232,33 @@ export type FaxiosInstance<TOpts = {}, TCaps = {}> = {
224
232
  <O, D = unknown>(url: string, data: D | undefined, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
225
233
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, data?: D, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
226
234
  };
227
- patchForm: {
235
+ query: {
228
236
  <O, D = unknown>(url: string, data: D | undefined, config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
229
237
  <T = unknown, R = FaxiosResponse<T>, D = unknown>(url: string, data?: D, config?: FaxiosRequestConfig<D> & TOpts): Promise<R>;
230
238
  };
231
- // `common` plus one header group per method; a key set directly on
232
- // `headers` applies to every method.
233
- defaults: Omit<FaxiosDefaults, "headers"> & {
234
- headers: HeadersDefaults & { [key: string]: FaxiosHeaderValue | undefined; };
235
- } & Partial<TOpts>;
236
- /**
237
- * Build the URL for `config` without sending a request. Applies `baseURL`, `params` and
238
- * `paramsSerializer`, but does not substitute `pathParams` or run schemas, so `{key}`
239
- * placeholders come back as written.
240
- */
241
- getUri: (config?: FaxiosRequestConfig & TOpts) => string;
242
- create: (instanceConfig?: CreateFaxiosDefaults) => FaxiosInstance;
239
+ request: {
240
+ <O, D = unknown>(config: SchemaConfig<O, D> & TOpts): Promise<FaxiosResponse<O, D>>;
241
+ <T = unknown, R = FaxiosResponse<T>, D = unknown>(config: FaxiosRequestConfig<D> & TOpts): Promise<R>;
242
+ };
243
243
  };
244
244
 
245
245
  /** The default export: an instance plus the static helpers only it carries. */
246
246
  export type FaxiosStatic = FaxiosInstance & {
247
- Faxios: typeof Faxios;
247
+ all: (promises: Array<Promise<unknown>>) => Promise<Array<unknown>>;
248
+ Cancel: typeof CanceledError;
248
249
  CanceledError: typeof CanceledError;
249
- isCancel: typeof isCancel;
250
- VERSION: typeof VERSION;
251
- toFormData: typeof toFormData;
250
+ default: FaxiosStatic;
251
+ Faxios: typeof Faxios;
252
252
  FaxiosError: typeof FaxiosError;
253
- Cancel: typeof CanceledError;
254
- all: (promises: Array<Promise<unknown>>) => Promise<Array<unknown>>;
255
- spread: (fn: (...args: Array<unknown>) => unknown) => (arr: Array<unknown>) => unknown;
256
- isFaxiosError: typeof isFaxiosError;
257
- mergeConfig: typeof mergeConfig;
258
253
  FaxiosHeaders: typeof FaxiosHeaders;
259
254
  formToJSON: (thing: unknown) => unknown;
260
255
  HttpStatusCode: typeof HttpStatusCode;
261
- default: FaxiosStatic;
256
+ isCancel: typeof isCancel;
257
+ isFaxiosError: typeof isFaxiosError;
258
+ mergeConfig: typeof mergeConfig;
259
+ spread: (fn: (...args: Array<unknown>) => unknown) => (arr: Array<unknown>) => unknown;
260
+ toFormData: typeof toFormData;
261
+ VERSION: typeof VERSION;
262
262
  };
263
263
 
264
264
  // Create the default instance to be exported; the statics are attached below.
@@ -71,7 +71,7 @@ const HttpStatusCode = {
71
71
  };
72
72
 
73
73
  Object.entries(HttpStatusCode).forEach(([ key, value ]) => {
74
- (HttpStatusCode as Record<string | number, string | number>)[value] = key;
74
+ (HttpStatusCode as Record<number | string, number | string>)[value] = key;
75
75
  });
76
76
 
77
77
  export default HttpStatusCode;
@@ -1,6 +1,5 @@
1
1
  import type { FaxiosRequestConfig } from "../types.js";
2
-
3
- export const DANGEROUS_KEYS = new Set([ "__proto__", "constructor", "prototype" ]);
2
+ import utils from "../utils.js";
4
3
 
5
4
  // ponytail: null-proto shallow clone; replaces mergeConfig({}, config) which ran the full
6
5
  // two-config merge machinery just to get a defensive copy. Security invariants preserved:
@@ -15,7 +14,7 @@ export default function cloneConfig<T extends FaxiosRequestConfig>(src: T): T &
15
14
  enumerable: false,
16
15
  }));
17
16
  for (const key of Object.keys(src)) {
18
- if (!DANGEROUS_KEYS.has(key)) {
17
+ if (!utils.isDangerousKey(key)) {
19
18
  (dst as Record<string, unknown>)[key] = (src as Record<string, unknown>)[key];
20
19
  }
21
20
  }
@@ -5,11 +5,11 @@ import utils from "../utils.js";
5
5
  type AbortHandler = (this: AbortSignal & { reason?: unknown; }, reason: unknown) => void;
6
6
  type EventListenerFn = (event: unknown) => void;
7
7
  type SignalLike = {
8
- unsubscribe?: (() => void) | ((handler: AbortHandler) => void);
9
- addEventListener: (type: string, listener: EventListenerFn) => void;
10
- removeEventListener: (type: string, listener: EventListenerFn) => void;
11
8
  aborted: boolean;
9
+ addEventListener: (type: string, listener: EventListenerFn) => void;
12
10
  reason?: unknown;
11
+ removeEventListener: (type: string, listener: EventListenerFn) => void;
12
+ unsubscribe?: (() => void) | ((handler: AbortHandler) => void);
13
13
  };
14
14
  export type ExtendedAbortSignal = AbortSignal & { unsubscribe?: (() => void) | ((handler: AbortHandler) => void); };
15
15
 
@@ -35,7 +35,7 @@ const composeSignals = (signals: Array<unknown> | null | undefined, timeout?: nu
35
35
  }
36
36
  };
37
37
 
38
- let timer: ReturnType<typeof setTimeout> | null =
38
+ let timer: null | ReturnType<typeof setTimeout> =
39
39
  timeout
40
40
  ? setTimeout(() => {
41
41
  timer = null;
@@ -2,9 +2,9 @@ import platform from "../platform.js";
2
2
  import utils from "../utils.js";
3
3
 
4
4
  type CookieManager = {
5
- write: (name: string, value: string, expires?: number, path?: string, domain?: string, secure?: boolean, sameSite?: string) => void;
6
- read: (name: string) => string | null;
5
+ read: (name: string) => null | string;
7
6
  remove: (name: string) => void;
7
+ write: (name: string, value: string, expires?: number, path?: string, domain?: string, secure?: boolean, sameSite?: string) => void;
8
8
  };
9
9
 
10
10
  const _globalThis = globalThis as { document?: { cookie?: string; }; };
@@ -36,7 +36,7 @@ export default platform.hasStandardBrowserEnv
36
36
  _globalThis.document.cookie = cookie.join("; ");
37
37
  },
38
38
 
39
- read(name: string): string | null {
39
+ read(name: string): null | string {
40
40
  if (!_globalThis.document) return null;
41
41
  // Match name=value by splitting on the semicolon separator instead of building a
42
42
  // RegExp from `name` — interpolating an unescaped string into a RegExp would let
@@ -29,7 +29,7 @@ function parsePropPath(name: string): Array<string> {
29
29
  // foo x y z
30
30
  const path: Array<string> = [];
31
31
  const pattern = /\w+|\[(\w*)]/g;
32
- let match: RegExpExecArray | null;
32
+ let match: null | RegExpExecArray;
33
33
 
34
34
  while ((match = pattern.exec(name)) !== null) {
35
35
  throwIfDepthExceeded(path.length);
@@ -66,13 +66,13 @@ function arrayToObject(arr: Array<unknown>): Record<string, unknown> {
66
66
  *
67
67
  * @returns {Object<string, any> | null} The converted object.
68
68
  */
69
- function formDataToJSON(formData: unknown): Record<string, unknown> | null {
69
+ function formDataToJSON(formData: unknown): null | Record<string, unknown> {
70
70
  function buildPath(path: Array<string>, value: unknown, target: Record<string, unknown>, index: number): boolean {
71
71
  throwIfDepthExceeded(index);
72
72
 
73
- let name: string | number = path[index++]!;
73
+ let name: number | string = path[index++]!;
74
74
 
75
- if (name === "__proto__") return true;
75
+ if (utils.isDangerousKey(name)) return true;
76
76
 
77
77
  const isNumericKey = Number.isFinite(+name);
78
78
  const isLast = index >= path.length;
@@ -38,8 +38,8 @@ const ignoreDuplicateOf = utils.toObjectSet([
38
38
  *
39
39
  * @returns {Object} Headers parsed into an object
40
40
  */
41
- export default (rawHeaders: string): Record<string, string | Array<string>> => {
42
- const parsed: Record<string, string | Array<string>> = {};
41
+ export default (rawHeaders: string): Record<string, Array<string> | string> => {
42
+ const parsed: Record<string, Array<string> | string> = {};
43
43
  let key: string;
44
44
  let val: string;
45
45
  let i: number;
@@ -11,7 +11,7 @@ export const progressEventReducer = (listener: (data: FaxiosProgressEvent) => vo
11
11
  if (!e || typeof (e as Record<string, unknown>).loaded !== "number") {
12
12
  return;
13
13
  }
14
- const ev = e as { loaded: number; total?: number; lengthComputable?: boolean; };
14
+ const ev = e as { lengthComputable?: boolean; loaded: number; total?: number; };
15
15
  const rawLoaded = ev.loaded;
16
16
  const total = ev.lengthComputable ? ev.total : undefined;
17
17
  const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded;
@@ -51,7 +51,20 @@ export const progressEventDecorator = (total: number | undefined, throttled: Ret
51
51
  ];
52
52
  };
53
53
 
54
+ // Progress listeners are caller code: any error they throw (or a promise they
55
+ // reject) is caught and ignored so it can't become an uncaught exception or
56
+ // change how the request settles.
54
57
  export const asyncDecorator =
55
58
  (fn: (...args: Array<unknown>) => unknown) =>
56
59
  (...args: Array<unknown>) =>
57
- utils.asap(() => fn(...args));
60
+ utils.asap(() => {
61
+ try {
62
+ const result = fn(...args);
63
+ if (utils.isThenable(result)) {
64
+ (result as PromiseLike<unknown>).then(undefined, () => {});
65
+ }
66
+ }
67
+ catch {
68
+ // Ignored by design: see the onUploadProgress/onDownloadProgress docs.
69
+ }
70
+ });
@@ -36,7 +36,7 @@ const INVALID_UNICODE_HEADER_VALUE_CHARS = new RegExp("[\\u0000-\\u0008\\u000a-\
36
36
  const INVALID_BYTE_STRING_HEADER_VALUE_CHARS = new RegExp("[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+", "g");
37
37
 
38
38
  // eslint-disable-next-line sonarjs/function-return-type
39
- function sanitizeValue(value: unknown, invalidChars: RegExp): string | Array<string> {
39
+ function sanitizeValue(value: unknown, invalidChars: RegExp): Array<string> | string {
40
40
  if (utils.isArray(value)) {
41
41
  return (value as Array<unknown>).map(item => sanitizeValue(item, invalidChars) as string);
42
42
  }
@@ -1,13 +1,23 @@
1
+ import FaxiosError from "../core/FaxiosError.js";
2
+
3
+ // `.` and `..` (raw or percent-encoded) would become a dot segment that the URL parser resolves,
4
+ // moving the request to another path.
5
+ const DOT_SEGMENT = /^(?:\.|%2e){1,2}$/i;
6
+
1
7
  /** Expects raw (unencoded) param values — applies encodeURIComponent here. */
2
8
  export function substitutePathParams(url: string, params: Record<string, unknown>): string {
3
9
  return url.replace(/\{([^{}]+)\}/g, (_match, key: string) => {
4
10
  if (!Object.prototype.hasOwnProperty.call(params, key)) {
5
- throw new Error(`Path param "${key}" not found in pathParams`);
11
+ throw new FaxiosError(`Path param "${key}" not found in pathParams`, FaxiosError.ERR_BAD_OPTION_VALUE);
6
12
  }
7
13
  const val = params[key];
8
14
  if (val == null) {
9
- throw new Error(`Path param "${key}" is null or undefined`);
15
+ throw new FaxiosError(`Path param "${key}" is null or undefined`, FaxiosError.ERR_BAD_OPTION_VALUE);
10
16
  }
11
- return encodeURIComponent(String(val));
17
+ const str = String(val);
18
+ if (DOT_SEGMENT.test(str)) {
19
+ throw new FaxiosError(`Path param "${key}" must not be "." or ".." (or a percent-encoded form)`, FaxiosError.ERR_BAD_OPTION_VALUE);
20
+ }
21
+ return encodeURIComponent(str);
12
22
  });
13
23
  }
@@ -8,7 +8,7 @@ function throttle(fn: (...args: Array<unknown>) => void, freq: number): [(...arg
8
8
  let timestamp = 0;
9
9
  const threshold = 1000 / freq;
10
10
  let lastArgs: Array<unknown> | null = null;
11
- let timer: ReturnType<typeof setTimeout> | null = null;
11
+ let timer: null | ReturnType<typeof setTimeout> = null;
12
12
 
13
13
  const invoke = (args: Array<unknown>, now: number = Date.now()) => {
14
14
  timestamp = now;
@@ -2,7 +2,7 @@
2
2
 
3
3
  import FaxiosError from "../core/FaxiosError.js";
4
4
  // temporary hotfix to avoid circular references until FaxiosURLSearchParams is refactored
5
- import type { GenericFormData, SerializerVisitor, FormDataVisitorHelpers } from "../types.js";
5
+ import type { FormDataVisitorHelpers, GenericFormData, SerializerVisitor } from "../types.js";
6
6
  import utils from "../utils.js";
7
7
 
8
8
  // Default nesting limit shared with the inverse transform (formDataToJSON) so
@@ -40,11 +40,11 @@ function removeBrackets(key: string): string {
40
40
  *
41
41
  * @returns {string} The path to the current key.
42
42
  */
43
- function renderKey(path: Array<string | number> | null | undefined, key: string | number, dots: unknown): string {
43
+ function renderKey(path: Array<number | string> | null | undefined, key: number | string, dots: unknown): string {
44
44
  if (!path) return String(key);
45
45
  return path
46
46
  .concat(key)
47
- .map(function each(token: string | number, i: number) {
47
+ .map(function each(token: number | string, i: number) {
48
48
  token = removeBrackets(String(token));
49
49
  return !dots && i ? "[" + token + "]" : token;
50
50
  })
@@ -91,7 +91,7 @@ const predicates = utils.toFlatObject(utils, {}, null as unknown as false, funct
91
91
  */
92
92
  function toFormData(obj: unknown, formData?: GenericFormData | null, options?: Record<string, unknown>): GenericFormData {
93
93
  if (!utils.isObject(obj)) {
94
- throw new TypeError("target must be an object");
94
+ throw new FaxiosError("target must be an object", FaxiosError.ERR_BAD_REQUEST);
95
95
  }
96
96
 
97
97
  const FormDataCtor = (globalThis as Record<string, unknown>)["FormData"] as (new () => GenericFormData) | undefined;
@@ -100,13 +100,15 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
100
100
  formData = new FormDataCtor();
101
101
  }
102
102
 
103
+ // Null-prototype destination: `visitor`, `maxDepth` and `Blob` are read below, and a polluted
104
+ // Object.prototype must not supply them.
103
105
  options = utils.toFlatObject(
104
106
  options,
105
- {
107
+ Object.assign(Object.create(null) as Record<string, unknown>, {
106
108
  metaTokens: true,
107
109
  dots: false,
108
110
  indexes: false,
109
- },
111
+ }),
110
112
  false,
111
113
  function defined(option: string, source: unknown) {
112
114
  return !utils.isUndefined((source as Record<string, unknown>)[option]);
@@ -118,16 +120,16 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
118
120
  const visitor = (options["visitor"] || defaultVisitor) as SerializerVisitor;
119
121
  const dots = options["dots"];
120
122
  const indexes = options["indexes"];
121
- const _Blob = options["Blob"] || (typeof (globalThis as Record<string, unknown>)["Blob"] !== "undefined" && (globalThis as Record<string, unknown>)["Blob"]);
123
+ const _Blob = (options["Blob"] || (globalThis as Record<string, unknown>)["Blob"]) as (new (parts: Array<unknown>) => unknown) | undefined;
122
124
  const maxDepth =
123
125
  options["maxDepth"] === undefined
124
126
  ? DEFAULT_FORM_DATA_MAX_DEPTH
125
127
  : (options["maxDepth"] as number);
126
- const useBlob = _Blob && utils.isSpecCompliantForm(formData);
128
+ const useBlob = typeof _Blob === "function" && utils.isSpecCompliantForm(formData);
127
129
  const stack: Array<unknown> = [];
128
130
 
129
131
  if (!utils.isFunction(visitor)) {
130
- throw new TypeError("visitor must be a function");
132
+ throw new FaxiosError("visitor must be a function", FaxiosError.ERR_BAD_OPTION_VALUE);
131
133
  }
132
134
 
133
135
  function convertValue(value: unknown): unknown {
@@ -141,16 +143,15 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
141
143
  return String(value);
142
144
  }
143
145
 
144
- if (!useBlob && utils.isBlob(value)) {
145
- throw new FaxiosError("Blob is not supported. Use a Buffer instead.");
146
+ if (!useBlob && (utils.isBlob(value) || utils.isArrayBuffer(value) || utils.isTypedArray(value))) {
147
+ throw new FaxiosError(
148
+ "Binary FormData values need a spec-compliant FormData and a Blob constructor (set the `Blob` option)",
149
+ FaxiosError.ERR_NOT_SUPPORT
150
+ );
146
151
  }
147
152
 
148
153
  if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
149
- const BlobCtor = (globalThis as Record<string, unknown>)["Blob"] as (new (parts: Array<unknown>) => unknown) | undefined;
150
- if (useBlob && typeof BlobCtor === "function") {
151
- return new BlobCtor([ value ]);
152
- }
153
- throw new FaxiosError("Blob is required for binary FormData values in this environment", FaxiosError.ERR_NOT_SUPPORT);
154
+ return new (_Blob as new (parts: Array<unknown>) => unknown)([ value ]);
154
155
  }
155
156
 
156
157
  return value;
@@ -201,7 +202,7 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
201
202
  *
202
203
  * @returns {boolean} return true to visit the each prop of the value recursively
203
204
  */
204
- function defaultVisitor(this: GenericFormData, value: unknown, key: string | number, path: null | Array<string | number>, _helpers?: FormDataVisitorHelpers): boolean {
205
+ function defaultVisitor(this: GenericFormData, value: unknown, key: number | string, path: Array<number | string> | null, _helpers?: FormDataVisitorHelpers): boolean {
205
206
  let arr: unknown = value;
206
207
 
207
208
  if (utils.isReactNative(formData) && utils.isReactNativeBlob(value)) {
@@ -248,19 +249,20 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
248
249
  return false;
249
250
  }
250
251
 
251
- const exposedHelpers: FormDataVisitorHelpers = Object.assign(predicates, {
252
- defaultVisitor: defaultVisitor,
252
+ const exposedHelpers: FormDataVisitorHelpers = {
253
+ ...predicates,
254
+ defaultVisitor,
253
255
  convertValue,
254
256
  isVisitable,
255
- });
257
+ };
256
258
 
257
- function build(value: unknown, path?: Array<string | number>, depth = 0): void {
259
+ function build(value: unknown, path?: Array<number | string>, depth = 0): void {
258
260
  if (utils.isUndefined(value)) return;
259
261
 
260
262
  throwIfMaxDepthExceeded(depth);
261
263
 
262
264
  if (stack.indexOf(value) !== -1) {
263
- throw new Error("Circular reference detected in " + (path ?? []).join("."));
265
+ throw new FaxiosError("Circular reference detected in " + (path ?? []).join("."), FaxiosError.ERR_BAD_REQUEST);
264
266
  }
265
267
 
266
268
  stack.push(value);
@@ -271,23 +273,19 @@ function toFormData(obj: unknown, formData?: GenericFormData | null, options?: R
271
273
  visitor.call(
272
274
  formData as GenericFormData,
273
275
  el,
274
- utils.isString(key) ? (key as string).trim() : key as string | number,
276
+ utils.isString(key) ? (key as string).trim() : key as number | string,
275
277
  path ?? null,
276
278
  exposedHelpers
277
279
  );
278
280
 
279
281
  if (result === true) {
280
- build(el, path ? path.concat(key as string | number) : [ key as string | number ], depth + 1);
282
+ build(el, path ? path.concat(key as number | string) : [ key as number | string ], depth + 1);
281
283
  }
282
284
  });
283
285
 
284
286
  stack.pop();
285
287
  }
286
288
 
287
- if (!utils.isObject(obj)) {
288
- throw new TypeError("data must be an object");
289
- }
290
-
291
289
  build(obj);
292
290
 
293
291
  return formData;
@@ -1,11 +1,14 @@
1
+ import FaxiosError from "../core/FaxiosError.js";
2
+ import utils from "../utils.js";
3
+
1
4
  type ReadableStreamController = {
2
- enqueue: (chunk: Uint8Array) => void;
3
5
  close: () => void;
6
+ enqueue: (chunk: Uint8Array) => void;
4
7
  };
5
8
 
6
9
  type ReadableStreamSource = {
7
- pull?: (controller: ReadableStreamController) => Promise<void>;
8
10
  cancel?: (reason?: unknown) => Promise<unknown>;
11
+ pull?: (controller: ReadableStreamController) => Promise<void>;
9
12
  };
10
13
 
11
14
  type ReadableStreamInit = {
@@ -16,9 +19,31 @@ type ReadableStreamCtor = new (source: ReadableStreamSource, init?: ReadableStre
16
19
 
17
20
  const ReadableStreamGlobal = (globalThis as Record<string, unknown>)["ReadableStream"] as ReadableStreamCtor | undefined;
18
21
 
19
- type ByteChunk = { byteLength: number; slice: (start: number, end?: number) => ByteChunk; };
22
+ const textEncoder = typeof TextEncoder === "function" ? new TextEncoder() : undefined;
23
+
24
+ // View the chunk's bytes without copying them. Strings are UTF-8 encoded; anything else is not
25
+ // a byte chunk and is rejected rather than silently converted.
26
+ const toBytes = (chunk: unknown): Uint8Array => {
27
+ if (chunk instanceof Uint8Array) {
28
+ return chunk;
29
+ }
30
+ if (utils.isArrayBufferView(chunk)) {
31
+ const view = chunk as ArrayBufferView;
32
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
33
+ }
34
+ if (utils.isArrayBuffer(chunk)) {
35
+ return new Uint8Array(chunk as ArrayBuffer);
36
+ }
37
+ if (utils.isString(chunk) && textEncoder) {
38
+ return textEncoder.encode(chunk as string);
39
+ }
40
+ throw new FaxiosError(
41
+ "Stream chunk must be a Uint8Array, another ArrayBufferView, an ArrayBuffer or a string, got " + utils.kindOf(chunk),
42
+ FaxiosError.ERR_BAD_REQUEST
43
+ );
44
+ };
20
45
 
21
- const streamChunk = function* (chunk: ByteChunk, chunkSize?: number): Generator<ByteChunk> {
46
+ const streamChunk = function* (chunk: Uint8Array, chunkSize?: number): Generator<Uint8Array> {
22
47
  const len = chunk.byteLength;
23
48
 
24
49
  if (!chunkSize || len < chunkSize) {
@@ -31,31 +56,31 @@ const streamChunk = function* (chunk: ByteChunk, chunkSize?: number): Generator<
31
56
 
32
57
  while (pos < len) {
33
58
  end = pos + chunkSize;
34
- yield chunk.slice(pos, end);
59
+ yield chunk.subarray(pos, end);
35
60
  pos = end;
36
61
  }
37
62
  };
38
63
 
39
- const readBytes = async function* (iterable: AsyncIterable<ByteChunk> | unknown, chunkSize?: number): AsyncGenerator<ByteChunk> {
64
+ const readBytes = async function* (iterable: unknown, chunkSize?: number): AsyncGenerator<Uint8Array> {
40
65
  for await (const chunk of readStream(iterable)) {
41
- yield* streamChunk(chunk, chunkSize);
66
+ yield* streamChunk(toBytes(chunk), chunkSize);
42
67
  }
43
68
  };
44
69
 
45
70
  type ReaderLike = {
46
- read: () => Promise<{ done: boolean; value: ByteChunk; }>;
47
71
  cancel: () => Promise<void>;
72
+ read: () => Promise<{ done: boolean; value: unknown; }>;
48
73
  };
49
74
 
50
75
  type StreamLike = {
51
- [Symbol.asyncIterator]?: () => AsyncIterator<ByteChunk>;
76
+ [Symbol.asyncIterator]?: () => AsyncIterator<unknown>;
52
77
  getReader?: () => ReaderLike;
53
78
  };
54
79
 
55
- const readStream = async function* (stream: unknown): AsyncGenerator<ByteChunk> {
80
+ const readStream = async function* (stream: unknown): AsyncGenerator<unknown> {
56
81
  const s = stream as StreamLike;
57
82
  if (s[Symbol.asyncIterator]) {
58
- yield* s as AsyncIterable<ByteChunk>;
83
+ yield* s as AsyncIterable<unknown>;
59
84
  return;
60
85
  }
61
86
 
@@ -104,9 +129,12 @@ export const trackStream = (stream: unknown, chunkSize: number | undefined, onPr
104
129
  const loadedBytes = (bytes += len);
105
130
  onProgress(loadedBytes);
106
131
  }
107
- controller.enqueue(new Uint8Array(value as unknown as ArrayBuffer));
132
+ controller.enqueue(value);
108
133
  }
109
134
  catch (err) {
135
+ // Cancel the source so its lock and connection are released. The original error wins
136
+ // over a failure to cancel.
137
+ await iterator.return(undefined).catch(() => {});
110
138
  _onFinish(err);
111
139
  throw err;
112
140
  }