axios 1.16.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.cts CHANGED
@@ -66,7 +66,9 @@ declare class AxiosHeaders {
66
66
  ...targets: Array<AxiosHeaders | axios.RawAxiosHeaders | string | undefined | null>
67
67
  ): AxiosHeaders;
68
68
 
69
- toJSON(asStrings?: boolean): axios.RawAxiosHeaders;
69
+ toJSON(asStrings: true): Record<string, string>;
70
+ toJSON(asStrings?: false): Record<string, string | string[]>;
71
+ toJSON(asStrings?: boolean): Record<string, string | string[]>;
70
72
 
71
73
  static from(thing?: AxiosHeaders | axios.RawAxiosHeaders | string): AxiosHeaders;
72
74
 
@@ -162,7 +164,9 @@ declare class AxiosError<T = unknown, D = any> extends Error {
162
164
  static readonly ETIMEDOUT = 'ETIMEDOUT';
163
165
  }
164
166
 
165
- declare class CanceledError<T> extends AxiosError<T> {}
167
+ declare class CanceledError<T> extends AxiosError<T> {
168
+ readonly name: 'CanceledError';
169
+ }
166
170
 
167
171
  declare class Axios {
168
172
  constructor(config?: axios.AxiosRequestConfig);
@@ -392,6 +396,8 @@ declare namespace axios {
392
396
  forcedJSONParsing?: boolean;
393
397
  clarifyTimeoutError?: boolean;
394
398
  legacyInterceptorReqResOrdering?: boolean;
399
+ advertiseZstdAcceptEncoding?: boolean;
400
+ validateStatusUndefinedResolves?: boolean;
395
401
  }
396
402
 
397
403
  interface GenericAbortSignal {
@@ -554,6 +560,7 @@ declare namespace axios {
554
560
  };
555
561
  formDataHeaderPolicy?: 'legacy' | 'content-only';
556
562
  redact?: string[];
563
+ sensitiveHeaders?: string[];
557
564
  }
558
565
 
559
566
  // Alias
@@ -691,7 +698,7 @@ declare namespace axios {
691
698
  CanceledError: typeof CanceledError;
692
699
  HttpStatusCode: typeof HttpStatusCode;
693
700
  readonly VERSION: string;
694
- isCancel(value: any): value is Cancel;
701
+ isCancel<T = any>(value: any): value is CanceledError<T>;
695
702
  all<T>(values: Array<T | Promise<T>>): Promise<T[]>;
696
703
  spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
697
704
  isAxiosError<T = any, D = any>(payload: any): payload is AxiosError<T, D>;
package/index.d.ts CHANGED
@@ -47,7 +47,9 @@ export class AxiosHeaders {
47
47
  ...targets: Array<AxiosHeaders | RawAxiosHeaders | string | undefined | null>
48
48
  ): AxiosHeaders;
49
49
 
50
- toJSON(asStrings?: boolean): RawAxiosHeaders;
50
+ toJSON(asStrings: true): Record<string, string>;
51
+ toJSON(asStrings?: false): Record<string, string | string[]>;
52
+ toJSON(asStrings?: boolean): Record<string, string | string[]>;
51
53
 
52
54
  static from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders;
53
55
 
@@ -281,6 +283,8 @@ export interface TransitionalOptions {
281
283
  forcedJSONParsing?: boolean;
282
284
  clarifyTimeoutError?: boolean;
283
285
  legacyInterceptorReqResOrdering?: boolean;
286
+ advertiseZstdAcceptEncoding?: boolean;
287
+ validateStatusUndefinedResolves?: boolean;
284
288
  }
285
289
 
286
290
  export interface GenericAbortSignal {
@@ -449,6 +453,7 @@ export interface AxiosRequestConfig<D = any> {
449
453
  };
450
454
  formDataHeaderPolicy?: 'legacy' | 'content-only';
451
455
  redact?: string[];
456
+ sensitiveHeaders?: string[];
452
457
  }
453
458
 
454
459
  // Alias
@@ -19,6 +19,35 @@ const DEFAULT_CHUNK_SIZE = 64 * 1024;
19
19
 
20
20
  const { isFunction } = utils;
21
21
 
22
+ /**
23
+ * Encode a UTF-8 string to a Latin-1 byte string for use with btoa().
24
+ * This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern.
25
+ *
26
+ * @param {string} str The string to encode
27
+ *
28
+ * @returns {string} UTF-8 bytes as a Latin-1 string
29
+ */
30
+ const encodeUTF8 = (str) =>
31
+ encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) =>
32
+ String.fromCharCode(parseInt(hex, 16))
33
+ );
34
+
35
+ // Node's WHATWG URL parser returns `username` and `password` percent-encoded.
36
+ // Decode before composing the `auth` option so credentials such as
37
+ // `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the
38
+ // original value for malformed input so a bad encoding never throws.
39
+ const decodeURIComponentSafe = (value) => {
40
+ if (!utils.isString(value)) {
41
+ return value;
42
+ }
43
+
44
+ try {
45
+ return decodeURIComponent(value);
46
+ } catch (error) {
47
+ return value;
48
+ }
49
+ };
50
+
22
51
  const test = (fn, ...args) => {
23
52
  try {
24
53
  return !!fn(...args);
@@ -27,6 +56,15 @@ const test = (fn, ...args) => {
27
56
  }
28
57
  };
29
58
 
59
+ const maybeWithAuthCredentials = (url) => {
60
+ const protocolIndex = url.indexOf('://');
61
+ let urlToCheck = url;
62
+ if (protocolIndex !== -1) {
63
+ urlToCheck = urlToCheck.slice(protocolIndex + 3);
64
+ }
65
+ return urlToCheck.includes('@') || urlToCheck.includes(':');
66
+ };
67
+
30
68
  const factory = (env) => {
31
69
  const globalObject =
32
70
  utils.global !== undefined && utils.global !== null
@@ -174,6 +212,7 @@ const factory = (env) => {
174
212
 
175
213
  const hasMaxContentLength = utils.isNumber(maxContentLength) && maxContentLength > -1;
176
214
  const hasMaxBodyLength = utils.isNumber(maxBodyLength) && maxBodyLength > -1;
215
+ const own = (key) => (utils.hasOwnProp(config, key) ? config[key] : undefined);
177
216
 
178
217
  let _fetch = envFetch || fetch;
179
218
 
@@ -195,7 +234,61 @@ const factory = (env) => {
195
234
 
196
235
  let requestContentLength;
197
236
 
237
+ // AxiosError we raise while the request body is being streamed. Captured
238
+ // by identity so the catch block can surface it directly, regardless of
239
+ // how the runtime wraps the resulting fetch rejection (undici exposes it
240
+ // as `err.cause`; some browsers drop the original error entirely).
241
+ let pendingBodyError = null;
242
+
243
+ const maxBodyLengthError = () =>
244
+ new AxiosError(
245
+ 'Request body larger than maxBodyLength limit',
246
+ AxiosError.ERR_BAD_REQUEST,
247
+ config,
248
+ request
249
+ );
250
+
198
251
  try {
252
+ // HTTP basic authentication
253
+ let auth = undefined;
254
+ const configAuth = own('auth');
255
+
256
+ if (configAuth) {
257
+ const username = utils.getSafeProp(configAuth, 'username') || '';
258
+ const password = utils.getSafeProp(configAuth, 'password') || '';
259
+ auth = {
260
+ username,
261
+ password
262
+ };
263
+ }
264
+
265
+ if (maybeWithAuthCredentials(url)) {
266
+ const parsedURL = new URL(url, platform.origin);
267
+
268
+ if (!auth && (parsedURL.username || parsedURL.password)) {
269
+ const urlUsername = decodeURIComponentSafe(parsedURL.username);
270
+ const urlPassword = decodeURIComponentSafe(parsedURL.password);
271
+ auth = {
272
+ username: urlUsername,
273
+ password: urlPassword
274
+ };
275
+ }
276
+
277
+ if (parsedURL.username || parsedURL.password) {
278
+ parsedURL.username = '';
279
+ parsedURL.password = '';
280
+ url = parsedURL.href;
281
+ }
282
+ }
283
+
284
+ if (auth) {
285
+ headers.delete('authorization');
286
+ headers.set(
287
+ 'Authorization',
288
+ 'Basic ' + btoa(encodeUTF8((auth.username || '') + ':' + (auth.password || '')))
289
+ );
290
+ }
291
+
199
292
  // Enforce maxContentLength for data: URLs up-front so we never materialize
200
293
  // an oversized payload. The HTTP adapter applies the same check (see http.js
201
294
  // "if (protocol === 'data:')" branch).
@@ -211,53 +304,96 @@ const factory = (env) => {
211
304
  }
212
305
  }
213
306
 
214
- // Enforce maxBodyLength against the outbound request body before dispatch.
215
- // Mirrors http.js behavior (ERR_BAD_REQUEST / 'Request body larger than
216
- // maxBodyLength limit'). Skip when the body length cannot be determined
217
- // (e.g. a live ReadableStream supplied by the caller).
307
+ // Enforce maxBodyLength against known-size bodies before dispatch using
308
+ // the body's *actual* size never a caller-declared Content-Length,
309
+ // which could under-report to slip an oversized body past the check.
310
+ // Unknown-size streams return undefined here and are counted per-chunk
311
+ // below as fetch consumes them.
218
312
  if (hasMaxBodyLength && method !== 'get' && method !== 'head') {
219
- const outboundLength = await resolveBodyLength(headers, data);
220
- if (
221
- typeof outboundLength === 'number' &&
222
- isFinite(outboundLength) &&
223
- outboundLength > maxBodyLength
224
- ) {
225
- throw new AxiosError(
226
- 'Request body larger than maxBodyLength limit',
227
- AxiosError.ERR_BAD_REQUEST,
228
- config,
229
- request
230
- );
313
+ const outboundLength = await getBodyLength(data);
314
+ if (typeof outboundLength === 'number' && isFinite(outboundLength)) {
315
+ requestContentLength = outboundLength;
316
+ if (outboundLength > maxBodyLength) {
317
+ throw maxBodyLengthError();
318
+ }
231
319
  }
232
320
  }
233
321
 
322
+ // A streamed body under maxBodyLength must be counted as fetch consumes
323
+ // it; its size is never trusted from a caller-declared Content-Length.
324
+ const mustEnforceStreamBody =
325
+ hasMaxBodyLength && (utils.isReadableStream(data) || utils.isStream(data));
326
+
327
+ const trackRequestStream = (stream, onProgress, flush) =>
328
+ trackStream(
329
+ stream,
330
+ DEFAULT_CHUNK_SIZE,
331
+ (loadedBytes) => {
332
+ if (hasMaxBodyLength && loadedBytes > maxBodyLength) {
333
+ throw (pendingBodyError = maxBodyLengthError());
334
+ }
335
+ onProgress && onProgress(loadedBytes);
336
+ },
337
+ flush
338
+ );
339
+
234
340
  if (
235
- onUploadProgress &&
236
341
  supportsRequestStream &&
237
342
  method !== 'get' &&
238
343
  method !== 'head' &&
239
- (requestContentLength = await resolveBodyLength(headers, data)) !== 0
344
+ (onUploadProgress || mustEnforceStreamBody)
240
345
  ) {
241
- let _request = new Request(url, {
242
- method: 'POST',
243
- body: data,
244
- duplex: 'half',
245
- });
346
+ requestContentLength =
347
+ requestContentLength == null ? await resolveBodyLength(headers, data) : requestContentLength;
348
+
349
+ // A declared length of 0 is only trusted to skip the wrap when we are
350
+ // not enforcing a stream limit (which must not rely on that header).
351
+ if (requestContentLength !== 0 || mustEnforceStreamBody) {
352
+ let _request = new Request(url, {
353
+ method: 'POST',
354
+ body: data,
355
+ duplex: 'half',
356
+ });
246
357
 
247
- let contentTypeHeader;
358
+ let contentTypeHeader;
248
359
 
249
- if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
250
- headers.setContentType(contentTypeHeader);
251
- }
360
+ if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
361
+ headers.setContentType(contentTypeHeader);
362
+ }
252
363
 
253
- if (_request.body) {
254
- const [onProgress, flush] = progressEventDecorator(
255
- requestContentLength,
256
- progressEventReducer(asyncDecorator(onUploadProgress))
257
- );
364
+ if (_request.body) {
365
+ const [onProgress, flush] =
366
+ (onUploadProgress &&
367
+ progressEventDecorator(
368
+ requestContentLength,
369
+ progressEventReducer(asyncDecorator(onUploadProgress))
370
+ )) ||
371
+ [];
258
372
 
259
- data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
373
+ data = trackRequestStream(_request.body, onProgress, flush);
374
+ }
260
375
  }
376
+ } else if (
377
+ mustEnforceStreamBody &&
378
+ !isRequestSupported &&
379
+ isReadableStreamSupported &&
380
+ method !== 'get' &&
381
+ method !== 'head'
382
+ ) {
383
+ data = trackRequestStream(data);
384
+ } else if (
385
+ mustEnforceStreamBody &&
386
+ isRequestSupported &&
387
+ !supportsRequestStream &&
388
+ method !== 'get' &&
389
+ method !== 'head'
390
+ ) {
391
+ throw new AxiosError(
392
+ 'Stream request bodies are not supported by the current fetch implementation',
393
+ AxiosError.ERR_NOT_SUPPORT,
394
+ config,
395
+ request
396
+ );
261
397
  }
262
398
 
263
399
  if (!utils.isString(withCredentials)) {
@@ -300,10 +436,12 @@ const factory = (env) => {
300
436
  ? _fetch(request, fetchOptions)
301
437
  : _fetch(url, resolvedOptions));
302
438
 
439
+ const responseHeaders = AxiosHeaders.from(response.headers);
440
+
303
441
  // Cheap pre-check: if the server honestly declares a content-length that
304
442
  // already exceeds the cap, reject before we start streaming.
305
443
  if (hasMaxContentLength) {
306
- const declaredLength = utils.toFiniteNumber(response.headers.get('content-length'));
444
+ const declaredLength = utils.toFiniteNumber(responseHeaders.getContentLength());
307
445
  if (declaredLength != null && declaredLength > maxContentLength) {
308
446
  throw new AxiosError(
309
447
  'maxContentLength size of ' + maxContentLength + ' exceeded',
@@ -328,7 +466,7 @@ const factory = (env) => {
328
466
  options[prop] = response[prop];
329
467
  });
330
468
 
331
- const responseContentLength = utils.toFiniteNumber(response.headers.get('content-length'));
469
+ const responseContentLength = utils.toFiniteNumber(responseHeaders.getContentLength());
332
470
 
333
471
  const [onProgress, flush] =
334
472
  (onDownloadProgress &&
@@ -423,6 +561,23 @@ const factory = (env) => {
423
561
  throw canceledError;
424
562
  }
425
563
 
564
+ // Surface a maxBodyLength violation we raised while the request body was
565
+ // being streamed. Matching by identity (rather than reading
566
+ // `err.cause.isAxiosError`) keeps the error deterministic across runtimes
567
+ // and avoids both prototype-pollution reads and mis-attributing a foreign
568
+ // AxiosError that merely happened to land in `err.cause`.
569
+ if (pendingBodyError) {
570
+ request && !pendingBodyError.request && (pendingBodyError.request = request);
571
+ throw pendingBodyError;
572
+ }
573
+
574
+ // Re-throw AxiosErrors we raised synchronously (data: URL / content-length
575
+ // pre-checks, response size enforcement) without re-wrapping them.
576
+ if (err instanceof AxiosError) {
577
+ request && !err.request && (err.request = request);
578
+ throw err;
579
+ }
580
+
426
581
  if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
427
582
  throw Object.assign(
428
583
  new AxiosError(