axios 1.15.0 → 1.15.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.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <h3 align="center"> 💎 Platinum sponsors <br> </h3> <table align="center"><tr><td align="center" width="50%"> <a href="https://thanks.dev/?utm_source&#x3D;axios&amp;utm_medium&#x3D;sponsorlist&amp;utm_campaign&#x3D;sponsorship" style="padding: 10px; display: inline-block" target="_blank"> <img width="90px" height="90px" src="https://axios-http.com/assets/sponsors/opencollective/ed51c2ee8f1b70aa3484d6dd678652134079a036.png" alt="THANKS.DEV"/> </a> <p align="center" title="We&#x27;re passionate about making open source sustainable. Scan your dependancy tree to better understand which open source projects need funding the most. Maintainers can also register their projects to become eligible for funding.">We&#x27;re passionate about making open source sustainable. Scan your dependancy tree to better understand which open source projects need funding the...</p> <p align="center"> <a href="https://thanks.dev/?utm_source&#x3D;axios&amp;utm_medium&#x3D;readme_sponsorlist&amp;utm_campaign&#x3D;sponsorship" target="_blank"><b>thanks.dev</b></a> </p>
2
- </td><td align="center" width="50%"> <a href="https://opencollective.com/hopper-security?utm_source&#x3D;axios&amp;utm_medium&#x3D;sponsorlist&amp;utm_campaign&#x3D;sponsorship" style="padding: 10px; display: inline-block" target="_blank"> <img width="90px" height="90px" src="https://axios-http.com/assets/sponsors/opencollective/180d02a83ee99448f850e39eed6dbb95f56000ba.png" alt="Hopper Security"/> </a> <p align="center"> </p>
2
+ </td><td align="center" width="50%"> <a href="https://opencollective.com/hopper-security?utm_source&#x3D;axios&amp;utm_medium&#x3D;sponsorlist&amp;utm_campaign&#x3D;sponsorship" style="padding: 10px; display: inline-block" target="_blank"> <img width="90px" height="90px" src="https://axios-http.com/assets/sponsors/opencollective/180d02a83ee99448f850e39eed6dbb95f56000ba.png" alt="Hopper Security"/> </a> <p align="center">Hopper provides a secure open-source registry where every component is verified against malware and continuously remediated for vulnerabilities across any version. In simple terms, Hopper removes the need to manage software supply chain risk altogether.</p><p align="center"> <a href="https://hopper.security/?utm_source&#x3D;axios&amp;utm_medium&#x3D;readme_sponsorlist&amp;utm_campaign&#x3D;sponsorship" target="_blank"><b>Hopper.Security</b></a> </p>
3
3
  </td></tr></table><table align="center"><tr><td align="center" width="50%"> <a href="https://opencollective.com/axios/contribute" target="_blank" >💜 Become a sponsor</a>
4
4
  </td><td align="center" width="50%"> <a href="https://opencollective.com/axios/contribute" target="_blank" >💜 Become a sponsor</a>
5
5
  </td></tr></table>
@@ -92,6 +92,8 @@
92
92
  - [Semver](#semver)
93
93
  - [Promises](#promises)
94
94
  - [TypeScript](#typescript)
95
+ - [Contributing](#contributing)
96
+ - [Local setup](#local-setup)
95
97
  - [Resources](#resources)
96
98
  - [Credits](#credits)
97
99
  - [License](#license)
@@ -127,12 +129,6 @@ Using npm:
127
129
  $ npm install axios
128
130
  ```
129
131
 
130
- Using bower:
131
-
132
- ```bash
133
- $ bower install axios
134
- ```
135
-
136
132
  Using yarn:
137
133
 
138
134
  ```bash
@@ -154,36 +150,36 @@ $ bun add axios
154
150
  Once the package is installed, you can import the library using `import` or `require` approach:
155
151
 
156
152
  ```js
157
- import axios, { isCancel, AxiosError } from "axios";
153
+ import axios, { isCancel, AxiosError } from 'axios';
158
154
  ```
159
155
 
160
156
  You can also use the default export, since the named export is just a re-export from the Axios factory:
161
157
 
162
158
  ```js
163
- import axios from "axios";
159
+ import axios from 'axios';
164
160
 
165
- console.log(axios.isCancel("something"));
161
+ console.log(axios.isCancel('something'));
166
162
  ```
167
163
 
168
164
  If you use `require` for importing, **only the default export is available**:
169
165
 
170
166
  ```js
171
- const axios = require("axios");
167
+ const axios = require('axios');
172
168
 
173
- console.log(axios.isCancel("something"));
169
+ console.log(axios.isCancel('something'));
174
170
  ```
175
171
 
176
172
  For some bundlers and some ES6 linters you may need to do the following:
177
173
 
178
174
  ```js
179
- import { default as axios } from "axios";
175
+ import { default as axios } from 'axios';
180
176
  ```
181
177
 
182
178
  For cases where something went wrong when trying to import a module into a custom or legacy environment,
183
179
  you can try importing the module package directly:
184
180
 
185
181
  ```js
186
- const axios = require("axios/dist/browser/axios.cjs"); // browser commonJS bundle (ES2017)
182
+ const axios = require('axios/dist/browser/axios.cjs'); // browser commonJS bundle (ES2017)
187
183
  // const axios = require('axios/dist/node/axios.cjs'); // node commonJS bundle (ES2017)
188
184
  ```
189
185
 
@@ -204,11 +200,11 @@ Using unpkg CDN:
204
200
  ## Example
205
201
 
206
202
  ```js
207
- import axios from "axios";
203
+ import axios from 'axios';
208
204
  //const axios = require('axios'); // legacy way
209
205
 
210
206
  try {
211
- const response = await axios.get("/user?ID=12345");
207
+ const response = await axios.get('/user?ID=12345');
212
208
  console.log(response);
213
209
  } catch (error) {
214
210
  console.error(error);
@@ -216,7 +212,7 @@ try {
216
212
 
217
213
  // Optionally the request above could also be done as
218
214
  axios
219
- .get("/user", {
215
+ .get('/user', {
220
216
  params: {
221
217
  ID: 12345,
222
218
  },
@@ -234,7 +230,7 @@ axios
234
230
  // Want to use async/await? Add the `async` keyword to your outer function/method.
235
231
  async function getUser() {
236
232
  try {
237
- const response = await axios.get("/user?ID=12345");
233
+ const response = await axios.get('/user?ID=12345');
238
234
  console.log(response);
239
235
  } catch (error) {
240
236
  console.error(error);
@@ -248,9 +244,9 @@ async function getUser() {
248
244
  Performing a `POST` request
249
245
 
250
246
  ```js
251
- const response = await axios.post("/user", {
252
- firstName: "Fred",
253
- lastName: "Flintstone",
247
+ const response = await axios.post('/user', {
248
+ firstName: 'Fred',
249
+ lastName: 'Flintstone',
254
250
  });
255
251
  console.log(response);
256
252
  ```
@@ -259,11 +255,11 @@ Performing multiple concurrent requests
259
255
 
260
256
  ```js
261
257
  function getUserAccount() {
262
- return axios.get("/user/12345");
258
+ return axios.get('/user/12345');
263
259
  }
264
260
 
265
261
  function getUserPermissions() {
266
- return axios.get("/user/12345/permissions");
262
+ return axios.get('/user/12345/permissions');
267
263
  }
268
264
 
269
265
  Promise.all([getUserAccount(), getUserPermissions()]).then(function (results) {
@@ -281,11 +277,11 @@ Requests can be made by passing the relevant config to `axios`.
281
277
  ```js
282
278
  // Send a POST request
283
279
  axios({
284
- method: "post",
285
- url: "/user/12345",
280
+ method: 'post',
281
+ url: '/user/12345',
286
282
  data: {
287
- firstName: "Fred",
288
- lastName: "Flintstone",
283
+ firstName: 'Fred',
284
+ lastName: 'Flintstone',
289
285
  },
290
286
  });
291
287
  ```
@@ -293,18 +289,18 @@ axios({
293
289
  ```js
294
290
  // GET request for remote image in node.js
295
291
  const response = await axios({
296
- method: "get",
297
- url: "https://bit.ly/2mTM3nY",
298
- responseType: "stream",
292
+ method: 'get',
293
+ url: 'https://bit.ly/2mTM3nY',
294
+ responseType: 'stream',
299
295
  });
300
- response.data.pipe(fs.createWriteStream("ada_lovelace.jpg"));
296
+ response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'));
301
297
  ```
302
298
 
303
299
  ##### axios(url[, config])
304
300
 
305
301
  ```js
306
302
  // Send a GET request (default method)
307
- axios("/user/12345");
303
+ axios('/user/12345');
308
304
  ```
309
305
 
310
306
  ### Request method aliases
@@ -348,9 +344,9 @@ You can create a new instance of axios with a custom config.
348
344
 
349
345
  ```js
350
346
  const instance = axios.create({
351
- baseURL: "https://some-domain.com/api/",
347
+ baseURL: 'https://some-domain.com/api/',
352
348
  timeout: 1000,
353
- headers: { "X-Custom-Header": "foobar" },
349
+ headers: { 'X-Custom-Header': 'foobar' },
354
350
  });
355
351
  ```
356
352
 
@@ -378,6 +374,19 @@ The available instance methods are listed below. The specified config will be me
378
374
 
379
375
  ## Request Config
380
376
 
377
+ ### ⚠️ Security notice: decompression-bomb protection is opt-in
378
+
379
+ By default `maxContentLength` and `maxBodyLength` are `-1` (unlimited). A malicious or compromised server can return a tiny gzip/deflate/brotli body that expands to gigabytes and exhaust the Node.js process.
380
+
381
+ If you call servers you do not fully trust, **set a cap**:
382
+
383
+ ```js
384
+ axios.defaults.maxContentLength = 10 * 1024 * 1024; // 10 MB
385
+ axios.defaults.maxBodyLength = 10 * 1024 * 1024;
386
+ ```
387
+
388
+ See the [security guide](https://axios.rest/pages/misc/security.html) for details.
389
+
381
390
  These are the available config options for making requests. Only the `url` is required. Requests will default to `GET` if `method` is not specified.
382
391
 
383
392
  ```js
@@ -436,7 +445,12 @@ These are the available config options for making requests. Only the `url` is re
436
445
  serialize?: (params: Record<string, any>, options?: ParamsSerializerOptions ),
437
446
 
438
447
  // Configuration for formatting array indexes in the params.
439
- indexes: false // Three available options: (1) indexes: null (leads to no brackets), (2) (default) indexes: false (leads to empty brackets), (3) indexes: true (leads to brackets with indexes).
448
+ indexes: false, // Three available options: (1) indexes: null (leads to no brackets), (2) (default) indexes: false (leads to empty brackets), (3) indexes: true (leads to brackets with indexes).
449
+
450
+ // Maximum object nesting depth when serializing params. Payloads deeper than this throw an
451
+ // AxiosError with code ERR_FORM_DATA_DEPTH_EXCEEDED. Default: 100. Set to Infinity to disable.
452
+ maxDepth: 100
453
+
440
454
  },
441
455
 
442
456
  // `data` is the data to be sent as the request body
@@ -586,8 +600,19 @@ These are the available config options for making requests. Only the `url` is re
586
600
  // e.g. '/var/run/docker.sock' to send requests to the docker daemon.
587
601
  // Only either `socketPath` or `proxy` can be specified.
588
602
  // If both are specified, `socketPath` is used.
603
+ //
604
+ // Security: when `socketPath` is set, hostname/port of the URL are ignored,
605
+ // which bypasses hostname-based SSRF protections. Never derive `socketPath`
606
+ // from untrusted input. Use `allowedSocketPaths` (below) to restrict accepted
607
+ // socket paths for defense-in-depth.
589
608
  socketPath: null, // default
590
609
 
610
+ // `allowedSocketPaths` restricts which `socketPath` values are accepted.
611
+ // Accepts a string or array of strings. Entries and the incoming socketPath
612
+ // are compared after path.resolve(). A mismatch throws AxiosError with code
613
+ // `ERR_BAD_OPTION_VALUE`. When null/undefined, no restriction is applied.
614
+ allowedSocketPaths: null, // default
615
+
591
616
  // `transport` determines the transport method that will be used to make the request.
592
617
  // If defined, it will be used. Otherwise, if `maxRedirects` is 0,
593
618
  // the default `http` or `https` library will be used, depending on the protocol specified in `protocol`.
@@ -679,6 +704,7 @@ These are the available config options for making requests. Only the `url` is re
679
704
  dots: boolean; // use dots instead of brackets format
680
705
  metaTokens: boolean; // keep special endings like {} in parameter key
681
706
  indexes: boolean; // array indexes format null - no brackets, false - empty brackets, true - brackets with indexes
707
+ maxDepth: 100; // maximum object nesting depth; throws AxiosError (ERR_FORM_DATA_DEPTH_EXCEEDED) if exceeded. Set to Infinity to disable.
682
708
  },
683
709
 
684
710
  // http adapter only (node.js)
@@ -688,6 +714,7 @@ These are the available config options for making requests. Only the `url` is re
688
714
  ]
689
715
  }
690
716
  ```
717
+
691
718
  ## 🔥 HTTP/2 Support
692
719
 
693
720
  Axios has experimental HTTP/2 support available via the Node.js HTTP adapter.
@@ -731,7 +758,7 @@ The response to a request contains the following information.
731
758
  When using `then`, you will receive the response as follows:
732
759
 
733
760
  ```js
734
- const response = await axios.get("/user/12345");
761
+ const response = await axios.get('/user/12345');
735
762
  console.log(response.data);
736
763
  console.log(response.status);
737
764
  console.log(response.statusText);
@@ -748,14 +775,13 @@ You can specify config defaults that will be applied to every request.
748
775
  ### Global axios defaults
749
776
 
750
777
  ```js
751
- axios.defaults.baseURL = "https://api.example.com";
778
+ axios.defaults.baseURL = 'https://api.example.com';
752
779
 
753
780
  // Important: If axios is used with multiple domains, the AUTH_TOKEN will be sent to all of them.
754
781
  // See below for an example using Custom instance defaults instead.
755
- axios.defaults.headers.common["Authorization"] = AUTH_TOKEN;
782
+ axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
756
783
 
757
- axios.defaults.headers.post["Content-Type"] =
758
- "application/x-www-form-urlencoded";
784
+ axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
759
785
  ```
760
786
 
761
787
  ### Custom instance defaults
@@ -763,11 +789,11 @@ axios.defaults.headers.post["Content-Type"] =
763
789
  ```js
764
790
  // Set config defaults when creating the instance
765
791
  const instance = axios.create({
766
- baseURL: "https://api.example.com",
792
+ baseURL: 'https://api.example.com',
767
793
  });
768
794
 
769
795
  // Alter defaults after instance has been created
770
- instance.defaults.headers.common["Authorization"] = AUTH_TOKEN;
796
+ instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
771
797
  ```
772
798
 
773
799
  ### Config order of precedence
@@ -784,7 +810,7 @@ const instance = axios.create();
784
810
  instance.defaults.timeout = 2500;
785
811
 
786
812
  // Override timeout for this request as it's known to take a long time
787
- instance.get("/longRequest", {
813
+ instance.get('/longRequest', {
788
814
  timeout: 5000,
789
815
  });
790
816
  ```
@@ -806,7 +832,7 @@ instance.interceptors.request.use(
806
832
  function (error) {
807
833
  // Do something with the request error
808
834
  return Promise.reject(error);
809
- },
835
+ }
810
836
  );
811
837
 
812
838
  // Add a response interceptor
@@ -820,7 +846,7 @@ instance.interceptors.response.use(
820
846
  // Any status codes that fall outside the range of 2xx cause this function to trigger
821
847
  // Do something with response error
822
848
  return Promise.reject(error);
823
- },
849
+ }
824
850
  );
825
851
  ```
826
852
 
@@ -865,11 +891,11 @@ to the options object that will tell axios to run the code synchronously and avo
865
891
  ```js
866
892
  axios.interceptors.request.use(
867
893
  function (config) {
868
- config.headers.test = "I am only a header!";
894
+ config.headers.test = 'I am only a header!';
869
895
  return config;
870
896
  },
871
897
  null,
872
- { synchronous: true },
898
+ { synchronous: true }
873
899
  );
874
900
  ```
875
901
 
@@ -881,15 +907,15 @@ asynchronous request interceptor that only needs to run at certain times.
881
907
 
882
908
  ```js
883
909
  function onGetCall(config) {
884
- return config.method === "get";
910
+ return config.method === 'get';
885
911
  }
886
912
  axios.interceptors.request.use(
887
913
  function (config) {
888
- config.headers.test = "special get headers";
914
+ config.headers.test = 'special get headers';
889
915
  return config;
890
916
  },
891
917
  null,
892
- { runWhen: onGetCall },
918
+ { runWhen: onGetCall }
893
919
  );
894
920
  ```
895
921
 
@@ -913,12 +939,12 @@ const interceptor = (id) => (base) => {
913
939
  return base;
914
940
  };
915
941
 
916
- instance.interceptors.request.use(interceptor("Request Interceptor 1"));
917
- instance.interceptors.request.use(interceptor("Request Interceptor 2"));
918
- instance.interceptors.request.use(interceptor("Request Interceptor 3"));
919
- instance.interceptors.response.use(interceptor("Response Interceptor 1"));
920
- instance.interceptors.response.use(interceptor("Response Interceptor 2"));
921
- instance.interceptors.response.use(interceptor("Response Interceptor 3"));
942
+ instance.interceptors.request.use(interceptor('Request Interceptor 1'));
943
+ instance.interceptors.request.use(interceptor('Request Interceptor 2'));
944
+ instance.interceptors.request.use(interceptor('Request Interceptor 3'));
945
+ instance.interceptors.response.use(interceptor('Response Interceptor 1'));
946
+ instance.interceptors.response.use(interceptor('Response Interceptor 2'));
947
+ instance.interceptors.response.use(interceptor('Response Interceptor 3'));
922
948
 
923
949
  // Console output:
924
950
  // Request Interceptor 3
@@ -982,7 +1008,7 @@ Below is a list of potential axios identified error:
982
1008
  The default behavior is to reject every response that returns with a status code that falls out of the range of 2xx and treat it as an error.
983
1009
 
984
1010
  ```js
985
- axios.get("/user/12345").catch(function (error) {
1011
+ axios.get('/user/12345').catch(function (error) {
986
1012
  if (error.response) {
987
1013
  // The request was made and the server responded with a status code
988
1014
  // that falls out of the range of 2xx
@@ -996,7 +1022,7 @@ axios.get("/user/12345").catch(function (error) {
996
1022
  console.log(error.request);
997
1023
  } else {
998
1024
  // Something happened in setting up the request that triggered an Error
999
- console.log("Error", error.message);
1025
+ console.log('Error', error.message);
1000
1026
  }
1001
1027
  console.log(error.config);
1002
1028
  });
@@ -1005,7 +1031,7 @@ axios.get("/user/12345").catch(function (error) {
1005
1031
  Using the `validateStatus` config option, you can override the default condition (status >= 200 && status < 300) and define HTTP code(s) that should throw an error.
1006
1032
 
1007
1033
  ```js
1008
- axios.get("/user/12345", {
1034
+ axios.get('/user/12345', {
1009
1035
  validateStatus: function (status) {
1010
1036
  return status < 500; // Resolve only if the status code is less than 500
1011
1037
  },
@@ -1015,7 +1041,7 @@ axios.get("/user/12345", {
1015
1041
  Using `toJSON` you get an object with more information about the HTTP error.
1016
1042
 
1017
1043
  ```js
1018
- axios.get("/user/12345").catch(function (error) {
1044
+ axios.get('/user/12345').catch(function (error) {
1019
1045
  console.log(error.toJSON());
1020
1046
  });
1021
1047
  ```
@@ -1025,7 +1051,7 @@ axios.get("/user/12345").catch(function (error) {
1025
1051
  ```js
1026
1052
  async function fetchWithTimeout() {
1027
1053
  try {
1028
- const response = await axios.get("https://example.com/data", {
1054
+ const response = await axios.get('https://example.com/data', {
1029
1055
  timeout: 5000, // 5 seconds
1030
1056
  transitional: {
1031
1057
  // set to true if you prefer ETIMEDOUT over ECONNABORTED
@@ -1033,19 +1059,19 @@ async function fetchWithTimeout() {
1033
1059
  },
1034
1060
  });
1035
1061
 
1036
- console.log("Response:", response.data);
1062
+ console.log('Response:', response.data);
1037
1063
  } catch (error) {
1038
1064
  if (axios.isAxiosError(error)) {
1039
- if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") {
1040
- console.error("Request timed out. Please try again.");
1065
+ if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') {
1066
+ console.error('Request timed out. Please try again.');
1041
1067
  return;
1042
1068
  }
1043
1069
 
1044
- console.error("Axios error:", error.message);
1070
+ console.error('Axios error:', error.message);
1045
1071
  return;
1046
1072
  }
1047
1073
 
1048
- console.error("Unexpected error:", error);
1074
+ console.error('Unexpected error:', error);
1049
1075
  }
1050
1076
  }
1051
1077
  ```
@@ -1060,7 +1086,7 @@ Starting from `v0.22.0` Axios supports AbortController to cancel requests in a f
1060
1086
  const controller = new AbortController();
1061
1087
 
1062
1088
  axios
1063
- .get("/foo/bar", {
1089
+ .get('/foo/bar', {
1064
1090
  signal: controller.signal,
1065
1091
  })
1066
1092
  .then(function (response) {
@@ -1085,29 +1111,29 @@ const CancelToken = axios.CancelToken;
1085
1111
  const source = CancelToken.source();
1086
1112
 
1087
1113
  axios
1088
- .get("/user/12345", {
1114
+ .get('/user/12345', {
1089
1115
  cancelToken: source.token,
1090
1116
  })
1091
1117
  .catch(function (thrown) {
1092
1118
  if (axios.isCancel(thrown)) {
1093
- console.log("Request canceled", thrown.message);
1119
+ console.log('Request canceled', thrown.message);
1094
1120
  } else {
1095
1121
  // handle error
1096
1122
  }
1097
1123
  });
1098
1124
 
1099
1125
  axios.post(
1100
- "/user/12345",
1126
+ '/user/12345',
1101
1127
  {
1102
- name: "new name",
1128
+ name: 'new name',
1103
1129
  },
1104
1130
  {
1105
1131
  cancelToken: source.token,
1106
- },
1132
+ }
1107
1133
  );
1108
1134
 
1109
1135
  // cancel the request (the message parameter is optional)
1110
- source.cancel("Operation canceled by the user.");
1136
+ source.cancel('Operation canceled by the user.');
1111
1137
  ```
1112
1138
 
1113
1139
  You can also create a cancel token by passing an executor function to the `CancelToken` constructor:
@@ -1116,7 +1142,7 @@ You can also create a cancel token by passing an executor function to the `Cance
1116
1142
  const CancelToken = axios.CancelToken;
1117
1143
  let cancel;
1118
1144
 
1119
- axios.get("/user/12345", {
1145
+ axios.get('/user/12345', {
1120
1146
  cancelToken: new CancelToken(function executor(c) {
1121
1147
  // An executor function receives a cancel function as a parameter
1122
1148
  cancel = c;
@@ -1139,9 +1165,9 @@ cancel();
1139
1165
  By default, axios serializes JavaScript objects to `JSON`. To send data in the [`application/x-www-form-urlencoded`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) format instead, you can use the [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) API, which is [supported](http://www.caniuse.com/#feat=urlsearchparams) in the vast majority of browsers, and [Node](https://nodejs.org/api/url.html#url_class_urlsearchparams) starting with v10 (released in 2018).
1140
1166
 
1141
1167
  ```js
1142
- const params = new URLSearchParams({ foo: "bar" });
1143
- params.append("extraparam", "value");
1144
- axios.post("/foo", params);
1168
+ const params = new URLSearchParams({ foo: 'bar' });
1169
+ params.append('extraparam', 'value');
1170
+ axios.post('/foo', params);
1145
1171
  ```
1146
1172
 
1147
1173
  ### Query string (Older browsers)
@@ -1151,18 +1177,18 @@ For compatibility with very old browsers, there is a [polyfill](https://github.c
1151
1177
  Alternatively, you can encode data using the [`qs`](https://github.com/ljharb/qs) library:
1152
1178
 
1153
1179
  ```js
1154
- const qs = require("qs");
1155
- axios.post("/foo", qs.stringify({ bar: 123 }));
1180
+ const qs = require('qs');
1181
+ axios.post('/foo', qs.stringify({ bar: 123 }));
1156
1182
  ```
1157
1183
 
1158
1184
  Or in another way (ES6),
1159
1185
 
1160
1186
  ```js
1161
- import qs from "qs";
1187
+ import qs from 'qs';
1162
1188
  const data = { bar: 123 };
1163
1189
  const options = {
1164
- method: "POST",
1165
- headers: { "content-type": "application/x-www-form-urlencoded" },
1190
+ method: 'POST',
1191
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
1166
1192
  data: qs.stringify(data),
1167
1193
  url,
1168
1194
  };
@@ -1174,8 +1200,8 @@ axios(options);
1174
1200
  For older Node.js engines, you can use the [`querystring`](https://nodejs.org/api/querystring.html) module as follows:
1175
1201
 
1176
1202
  ```js
1177
- const querystring = require("querystring");
1178
- axios.post("https://something.com/", querystring.stringify({ foo: "bar" }));
1203
+ const querystring = require('querystring');
1204
+ axios.post('https://something.com/', querystring.stringify({ foo: 'bar' }));
1179
1205
  ```
1180
1206
 
1181
1207
  You can also use the [`qs`](https://github.com/ljharb/qs) library.
@@ -1192,13 +1218,13 @@ const data = {
1192
1218
  arr: [1, 2, 3],
1193
1219
  arr2: [1, [2], 3],
1194
1220
  users: [
1195
- { name: "Peter", surname: "Griffin" },
1196
- { name: "Thomas", surname: "Anderson" },
1221
+ { name: 'Peter', surname: 'Griffin' },
1222
+ { name: 'Thomas', surname: 'Anderson' },
1197
1223
  ],
1198
1224
  };
1199
1225
 
1200
- await axios.postForm("https://postman-echo.com/post", data, {
1201
- headers: { "content-type": "application/x-www-form-urlencoded" },
1226
+ await axios.postForm('https://postman-echo.com/post', data, {
1227
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
1202
1228
  });
1203
1229
  ```
1204
1230
 
@@ -1226,7 +1252,7 @@ const app = express();
1226
1252
 
1227
1253
  app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
1228
1254
 
1229
- app.post("/", function (req, res, next) {
1255
+ app.post('/', function (req, res, next) {
1230
1256
  // echo body as JSON
1231
1257
  res.send(JSON.stringify(req.body));
1232
1258
  });
@@ -1243,22 +1269,22 @@ Setting the `Content-Type` header is not required as Axios guesses it based on t
1243
1269
 
1244
1270
  ```js
1245
1271
  const formData = new FormData();
1246
- formData.append("foo", "bar");
1272
+ formData.append('foo', 'bar');
1247
1273
 
1248
- axios.post("https://httpbin.org/post", formData);
1274
+ axios.post('https://httpbin.org/post', formData);
1249
1275
  ```
1250
1276
 
1251
1277
  In node.js, you can use the [`form-data`](https://github.com/form-data/form-data) library as follows:
1252
1278
 
1253
1279
  ```js
1254
- const FormData = require("form-data");
1280
+ const FormData = require('form-data');
1255
1281
 
1256
1282
  const form = new FormData();
1257
- form.append("my_field", "my value");
1258
- form.append("my_buffer", Buffer.alloc(10));
1259
- form.append("my_file", fs.createReadStream("/foo/bar.jpg"));
1283
+ form.append('my_field', 'my value');
1284
+ form.append('my_buffer', Buffer.alloc(10));
1285
+ form.append('my_file', fs.createReadStream('/foo/bar.jpg'));
1260
1286
 
1261
- axios.post("https://example.com", form);
1287
+ axios.post('https://example.com', form);
1262
1288
  ```
1263
1289
 
1264
1290
  ### 🆕 Automatic serialization to FormData
@@ -1269,17 +1295,17 @@ header is set to `multipart/form-data`.
1269
1295
  The following request will submit the data in a FormData format (Browser & Node.js):
1270
1296
 
1271
1297
  ```js
1272
- import axios from "axios";
1298
+ import axios from 'axios';
1273
1299
 
1274
1300
  axios
1275
1301
  .post(
1276
- "https://httpbin.org/post",
1302
+ 'https://httpbin.org/post',
1277
1303
  { x: 1 },
1278
1304
  {
1279
1305
  headers: {
1280
- "Content-Type": "multipart/form-data",
1306
+ 'Content-Type': 'multipart/form-data',
1281
1307
  },
1282
- },
1308
+ }
1283
1309
  )
1284
1310
  .then(({ data }) => console.log(data));
1285
1311
  ```
@@ -1290,18 +1316,18 @@ You can overload the FormData class by setting the `env.FormData` config variabl
1290
1316
  but you probably won't need it in most cases:
1291
1317
 
1292
1318
  ```js
1293
- const axios = require("axios");
1294
- var FormData = require("form-data");
1319
+ const axios = require('axios');
1320
+ var FormData = require('form-data');
1295
1321
 
1296
1322
  axios
1297
1323
  .post(
1298
- "https://httpbin.org/post",
1324
+ 'https://httpbin.org/post',
1299
1325
  { x: 1, buf: Buffer.alloc(10) },
1300
1326
  {
1301
1327
  headers: {
1302
- "Content-Type": "multipart/form-data",
1328
+ 'Content-Type': 'multipart/form-data',
1303
1329
  },
1304
- },
1330
+ }
1305
1331
  )
1306
1332
  .then(({ data }) => console.log(data));
1307
1333
  ```
@@ -1327,6 +1353,18 @@ FormData serializer supports additional options via `config.formSerializer: obje
1327
1353
  - `null` - don't add brackets (`arr: 1`, `arr: 2`, `arr: 3`)
1328
1354
  - `false`(default) - add empty brackets (`arr[]: 1`, `arr[]: 2`, `arr[]: 3`)
1329
1355
  - `true` - add brackets with indexes (`arr[0]: 1`, `arr[1]: 2`, `arr[2]: 3`)
1356
+ - `maxDepth: number = 100` - maximum object nesting depth the serializer will recurse into. If the
1357
+ input object exceeds this depth, an `AxiosError` with `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` is
1358
+ thrown instead of overflowing the call stack. This protects server-side applications from DoS
1359
+ attacks via deeply nested payloads. Set to `Infinity` to disable the limit and restore pre-fix behaviour.
1360
+
1361
+ ```js
1362
+ // Raise the limit for a schema that genuinely nests deeper than 100 levels:
1363
+ axios.postForm('/api', data, { formSerializer: { maxDepth: 200 } });
1364
+
1365
+ // Same protection applies to params serialization:
1366
+ axios.get('/api', { params: data, paramsSerializer: { maxDepth: 200 } });
1367
+ ```
1330
1368
 
1331
1369
  Let's say we have an object like this one:
1332
1370
 
@@ -1336,10 +1374,10 @@ const obj = {
1336
1374
  arr: [1, 2, 3],
1337
1375
  arr2: [1, [2], 3],
1338
1376
  users: [
1339
- { name: "Peter", surname: "Griffin" },
1340
- { name: "Thomas", surname: "Anderson" },
1377
+ { name: 'Peter', surname: 'Griffin' },
1378
+ { name: 'Thomas', surname: 'Anderson' },
1341
1379
  ],
1342
- "obj2{}": [{ x: 1 }],
1380
+ 'obj2{}': [{ x: 1 }],
1343
1381
  };
1344
1382
  ```
1345
1383
 
@@ -1347,18 +1385,18 @@ The following steps will be executed by the Axios serializer internally:
1347
1385
 
1348
1386
  ```js
1349
1387
  const formData = new FormData();
1350
- formData.append("x", "1");
1351
- formData.append("arr[]", "1");
1352
- formData.append("arr[]", "2");
1353
- formData.append("arr[]", "3");
1354
- formData.append("arr2[0]", "1");
1355
- formData.append("arr2[1][0]", "2");
1356
- formData.append("arr2[2]", "3");
1357
- formData.append("users[0][name]", "Peter");
1358
- formData.append("users[0][surname]", "Griffin");
1359
- formData.append("users[1][name]", "Thomas");
1360
- formData.append("users[1][surname]", "Anderson");
1361
- formData.append("obj2{}", '[{"x":1}]');
1388
+ formData.append('x', '1');
1389
+ formData.append('arr[]', '1');
1390
+ formData.append('arr[]', '2');
1391
+ formData.append('arr[]', '3');
1392
+ formData.append('arr2[0]', '1');
1393
+ formData.append('arr2[1][0]', '2');
1394
+ formData.append('arr2[2]', '3');
1395
+ formData.append('users[0][name]', 'Peter');
1396
+ formData.append('users[0][surname]', 'Griffin');
1397
+ formData.append('users[1][name]', 'Thomas');
1398
+ formData.append('users[1][surname]', 'Anderson');
1399
+ formData.append('obj2{}', '[{"x":1}]');
1362
1400
  ```
1363
1401
 
1364
1402
  Axios supports the following shortcut methods: `postForm`, `putForm`, `patchForm`
@@ -1369,27 +1407,24 @@ which are just the corresponding http methods with the `Content-Type` header pre
1369
1407
  You can easily submit a single file:
1370
1408
 
1371
1409
  ```js
1372
- await axios.postForm("https://httpbin.org/post", {
1373
- myVar: "foo",
1374
- file: document.querySelector("#fileInput").files[0],
1410
+ await axios.postForm('https://httpbin.org/post', {
1411
+ myVar: 'foo',
1412
+ file: document.querySelector('#fileInput').files[0],
1375
1413
  });
1376
1414
  ```
1377
1415
 
1378
1416
  or multiple files as `multipart/form-data`:
1379
1417
 
1380
1418
  ```js
1381
- await axios.postForm("https://httpbin.org/post", {
1382
- "files[]": document.querySelector("#fileInput").files,
1419
+ await axios.postForm('https://httpbin.org/post', {
1420
+ 'files[]': document.querySelector('#fileInput').files,
1383
1421
  });
1384
1422
  ```
1385
1423
 
1386
1424
  `FileList` object can be passed directly:
1387
1425
 
1388
1426
  ```js
1389
- await axios.postForm(
1390
- "https://httpbin.org/post",
1391
- document.querySelector("#fileInput").files,
1392
- );
1427
+ await axios.postForm('https://httpbin.org/post', document.querySelector('#fileInput').files);
1393
1428
  ```
1394
1429
 
1395
1430
  All files will be sent with the same field names: `files[]`.
@@ -1399,24 +1434,17 @@ All files will be sent with the same field names: `files[]`.
1399
1434
  Pass an HTML Form element as a payload to submit it as `multipart/form-data` content.
1400
1435
 
1401
1436
  ```js
1402
- await axios.postForm(
1403
- "https://httpbin.org/post",
1404
- document.querySelector("#htmlForm"),
1405
- );
1437
+ await axios.postForm('https://httpbin.org/post', document.querySelector('#htmlForm'));
1406
1438
  ```
1407
1439
 
1408
1440
  `FormData` and `HTMLForm` objects can also be posted as `JSON` by explicitly setting the `Content-Type` header to `application/json`:
1409
1441
 
1410
1442
  ```js
1411
- await axios.post(
1412
- "https://httpbin.org/post",
1413
- document.querySelector("#htmlForm"),
1414
- {
1415
- headers: {
1416
- "Content-Type": "application/json",
1417
- },
1443
+ await axios.post('https://httpbin.org/post', document.querySelector('#htmlForm'), {
1444
+ headers: {
1445
+ 'Content-Type': 'application/json',
1418
1446
  },
1419
- );
1447
+ });
1420
1448
  ```
1421
1449
 
1422
1450
  For example, the Form
@@ -1503,7 +1531,7 @@ const { data } = await axios.post(SERVER_URL, readableStream, {
1503
1531
  },
1504
1532
 
1505
1533
  headers: {
1506
- "Content-Length": contentLength,
1534
+ 'Content-Length': contentLength,
1507
1535
  },
1508
1536
 
1509
1537
  maxRedirects: 0, // avoid buffering the entire stream
@@ -1524,9 +1552,7 @@ Download and upload rate limits can only be set for the http adapter (node.js):
1524
1552
  ```js
1525
1553
  const { data } = await axios.post(LOCAL_SERVER_URL, myBuffer, {
1526
1554
  onUploadProgress: ({ progress, rate }) => {
1527
- console.log(
1528
- `Upload [${(progress * 100).toFixed(2)}%]: ${(rate / 1024).toFixed(2)}KB/s`,
1529
- );
1555
+ console.log(`Upload [${(progress * 100).toFixed(2)}%]: ${(rate / 1024).toFixed(2)}KB/s`);
1530
1556
  },
1531
1557
 
1532
1558
  maxRate: [100 * 1024], // 100KB/s limit
@@ -1561,18 +1587,18 @@ The headers object is always initialized inside interceptors and transformers:
1561
1587
 
1562
1588
  ```ts
1563
1589
  axios.interceptors.request.use((request: InternalAxiosRequestConfig) => {
1564
- request.headers.set("My-header", "value");
1590
+ request.headers.set('My-header', 'value');
1565
1591
 
1566
1592
  request.headers.set({
1567
- "My-set-header1": "my-set-value1",
1568
- "My-set-header2": "my-set-value2",
1593
+ 'My-set-header1': 'my-set-value1',
1594
+ 'My-set-header2': 'my-set-value2',
1569
1595
  });
1570
1596
 
1571
- request.headers.set("User-Agent", false); // disable subsequent setting the header by Axios
1597
+ request.headers.set('User-Agent', false); // disable subsequent setting the header by Axios
1572
1598
 
1573
- request.headers.setContentType("text/plain");
1599
+ request.headers.setContentType('text/plain');
1574
1600
 
1575
- request.headers["My-set-header2"] = "newValue"; // direct access is deprecated
1601
+ request.headers['My-set-header2'] = 'newValue'; // direct access is deprecated
1576
1602
 
1577
1603
  return request;
1578
1604
  });
@@ -1582,9 +1608,9 @@ You can iterate over an `AxiosHeaders` instance using a `for...of` statement:
1582
1608
 
1583
1609
  ```js
1584
1610
  const headers = new AxiosHeaders({
1585
- foo: "1",
1586
- bar: "2",
1587
- baz: "3",
1611
+ foo: '1',
1612
+ bar: '2',
1613
+ baz: '3',
1588
1614
  });
1589
1615
 
1590
1616
  for (const [header, value] of headers) {
@@ -1683,26 +1709,26 @@ matcher function or internal key-value parser.
1683
1709
 
1684
1710
  ```ts
1685
1711
  const headers = new AxiosHeaders({
1686
- "Content-Type": "multipart/form-data; boundary=Asrf456BGe4h",
1712
+ 'Content-Type': 'multipart/form-data; boundary=Asrf456BGe4h',
1687
1713
  });
1688
1714
 
1689
- console.log(headers.get("Content-Type"));
1715
+ console.log(headers.get('Content-Type'));
1690
1716
  // multipart/form-data; boundary=Asrf456BGe4h
1691
1717
 
1692
- console.log(headers.get("Content-Type", true)); // parse key-value pairs from a string separated with \s,;= delimiters:
1718
+ console.log(headers.get('Content-Type', true)); // parse key-value pairs from a string separated with \s,;= delimiters:
1693
1719
  // [Object: null prototype] {
1694
1720
  // 'multipart/form-data': undefined,
1695
1721
  // boundary: 'Asrf456BGe4h'
1696
1722
  // }
1697
1723
 
1698
1724
  console.log(
1699
- headers.get("Content-Type", (value, name, headers) => {
1700
- return String(value).replace(/a/g, "ZZZ");
1701
- }),
1725
+ headers.get('Content-Type', (value, name, headers) => {
1726
+ return String(value).replace(/a/g, 'ZZZ');
1727
+ })
1702
1728
  );
1703
1729
  // multipZZZrt/form-dZZZtZZZ; boundZZZry=Asrf456BGe4h
1704
1730
 
1705
- console.log(headers.get("Content-Type", /boundary=(\w+)/)?.[0]);
1731
+ console.log(headers.get('Content-Type', /boundary=(\w+)/)?.[0]);
1706
1732
  // boundary=Asrf456BGe4h
1707
1733
  ```
1708
1734
 
@@ -1735,9 +1761,9 @@ Unlike the `delete` method matcher, this optional matcher will be used to match
1735
1761
 
1736
1762
  ```ts
1737
1763
  const headers = new AxiosHeaders({
1738
- foo: "1",
1739
- "x-foo": "2",
1740
- "x-bar": "3",
1764
+ foo: '1',
1765
+ 'x-foo': '2',
1766
+ 'x-bar': '3',
1741
1767
  });
1742
1768
 
1743
1769
  console.log(headers.clear(/^x-/)); // true
@@ -1756,11 +1782,11 @@ Set `format` to true for converting header names to lowercase and capitalizing t
1756
1782
 
1757
1783
  ```js
1758
1784
  const headers = new AxiosHeaders({
1759
- foo: "1",
1785
+ foo: '1',
1760
1786
  });
1761
1787
 
1762
- headers.Foo = "2";
1763
- headers.FOO = "3";
1788
+ headers.Foo = '2';
1789
+ headers.FOO = '3';
1764
1790
 
1765
1791
  console.log(headers.toJSON()); // [Object: null prototype] { foo: '1', Foo: '2', FOO: '3' }
1766
1792
  console.log(headers.normalize().toJSON()); // [Object: null prototype] { foo: '3' }
@@ -1827,7 +1853,7 @@ To use it by default, it must be selected explicitly:
1827
1853
 
1828
1854
  ```js
1829
1855
  const { data } = axios.get(url, {
1830
- adapter: "fetch", // by default ['xhr', 'http', 'fetch']
1856
+ adapter: 'fetch', // by default ['xhr', 'http', 'fetch']
1831
1857
  });
1832
1858
  ```
1833
1859
 
@@ -1835,7 +1861,7 @@ You can create a separate instance for this:
1835
1861
 
1836
1862
  ```js
1837
1863
  const fetchAxios = axios.create({
1838
- adapter: "fetch",
1864
+ adapter: 'fetch',
1839
1865
  });
1840
1866
 
1841
1867
  const { data } = fetchAxios.get(url);
@@ -1859,12 +1885,12 @@ you must disable their use inside the fetch adapter by passing null.
1859
1885
  Basic example:
1860
1886
 
1861
1887
  ```js
1862
- import customFetchFunction from "customFetchModule";
1888
+ import customFetchFunction from 'customFetchModule';
1863
1889
 
1864
1890
  const instance = axios.create({
1865
- adapter: "fetch",
1891
+ adapter: 'fetch',
1866
1892
  onDownloadProgress(e) {
1867
- console.log("downloadProgress", e);
1893
+ console.log('downloadProgress', e);
1868
1894
  },
1869
1895
  env: {
1870
1896
  fetch: customFetchFunction,
@@ -1879,20 +1905,20 @@ const instance = axios.create({
1879
1905
  A minimal example of setting up Axios for use in a [Tauri](https://tauri.app/plugin/http-client/) app with a platform fetch function that ignores CORS policy for requests.
1880
1906
 
1881
1907
  ```js
1882
- import { fetch } from "@tauri-apps/plugin-http";
1883
- import axios from "axios";
1908
+ import { fetch } from '@tauri-apps/plugin-http';
1909
+ import axios from 'axios';
1884
1910
 
1885
1911
  const instance = axios.create({
1886
- adapter: "fetch",
1912
+ adapter: 'fetch',
1887
1913
  onDownloadProgress(e) {
1888
- console.log("downloadProgress", e);
1914
+ console.log('downloadProgress', e);
1889
1915
  },
1890
1916
  env: {
1891
1917
  fetch,
1892
1918
  },
1893
1919
  });
1894
1920
 
1895
- const { data } = await instance.get("https://google.com");
1921
+ const { data } = await instance.get('https://google.com');
1896
1922
  ```
1897
1923
 
1898
1924
  #### 🔥 Using with SvelteKit
@@ -1902,17 +1928,14 @@ which makes it incompatible with the standard URL API. So, Axios must be configu
1902
1928
 
1903
1929
  ```js
1904
1930
  export async function load({ fetch }) {
1905
- const { data: post } = await axios.get(
1906
- "https://jsonplaceholder.typicode.com/posts/1",
1907
- {
1908
- adapter: "fetch",
1909
- env: {
1910
- fetch,
1911
- Request: null,
1912
- Response: null,
1913
- },
1931
+ const { data: post } = await axios.get('https://jsonplaceholder.typicode.com/posts/1', {
1932
+ adapter: 'fetch',
1933
+ env: {
1934
+ fetch,
1935
+ Request: null,
1936
+ Response: null,
1914
1937
  },
1915
- );
1938
+ });
1916
1939
 
1917
1940
  return { post };
1918
1941
  }
@@ -1931,21 +1954,17 @@ Note: HTTP/2 redirects are currently not supported by the HTTP/2 adapter.
1931
1954
  ```js
1932
1955
  const form = new FormData();
1933
1956
 
1934
- form.append("foo", "123");
1957
+ form.append('foo', '123');
1935
1958
 
1936
- const { data, headers, status } = await axios.post(
1937
- "https://httpbin.org/post",
1938
- form,
1939
- {
1940
- onUploadProgress(e) {
1941
- console.log("upload progress", e);
1942
- },
1943
- onDownloadProgress(e) {
1944
- console.log("download progress", e);
1945
- },
1946
- responseType: "arraybuffer",
1947
- }
1948
- );
1959
+ const { data, headers, status } = await axios.post('https://httpbin.org/post', form, {
1960
+ onUploadProgress(e) {
1961
+ console.log('upload progress', e);
1962
+ },
1963
+ onDownloadProgress(e) {
1964
+ console.log('download progress', e);
1965
+ },
1966
+ responseType: 'arraybuffer',
1967
+ });
1949
1968
  ```
1950
1969
 
1951
1970
  ## Semver
@@ -1964,7 +1983,7 @@ axios includes [TypeScript](https://typescriptlang.org) definitions and a type g
1964
1983
  ```typescript
1965
1984
  let user: User = null;
1966
1985
  try {
1967
- const { data } = await axios.get("/user?ID=12345");
1986
+ const { data } = await axios.get('/user?ID=12345');
1968
1987
  user = data.userDetails;
1969
1988
  } catch (error) {
1970
1989
  if (axios.isAxiosError(error)) {
@@ -1984,10 +2003,10 @@ If you use TypeScript to type check CJS JavaScript code, your only option is to
1984
2003
  You can also create a custom instance with typed interceptors:
1985
2004
 
1986
2005
  ```typescript
1987
- import axios, { AxiosInstance, InternalAxiosRequestConfig } from "axios";
2006
+ import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
1988
2007
 
1989
2008
  const apiClient: AxiosInstance = axios.create({
1990
- baseURL: "https://api.example.com",
2009
+ baseURL: 'https://api.example.com',
1991
2010
  timeout: 10000,
1992
2011
  });
1993
2012
 
@@ -2003,6 +2022,23 @@ You can use Gitpod, an online IDE(which is free for Open Source) for contributin
2003
2022
 
2004
2023
  [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/axios/axios/blob/main/examples/server.js)
2005
2024
 
2025
+ ## Contributing
2026
+
2027
+ ### Local setup
2028
+
2029
+ As a supply-chain hardening measure, this repository ships a project-level `.npmrc` that sets `ignore-scripts=true`. This blocks npm lifecycle scripts (`preinstall`, `install`, `postinstall`, `prepare`) from any direct or transitive dependency when you run `npm install` or `npm ci` inside the repo. See [THREATMODEL.md](./THREATMODEL.md) (threat T-S2) for the rationale.
2030
+
2031
+ One consequence: the repository's own `prepare` hook (which installs Husky's git hooks) will **not** run automatically. After your first install, enable the git hooks manually:
2032
+
2033
+ ```bash
2034
+ npm ci
2035
+ npm rebuild husky && npx husky
2036
+ ```
2037
+
2038
+ Run those two commands once per fresh checkout. You do **not** need to re-run them after every subsequent `npm install`.
2039
+
2040
+ Do not remove `ignore-scripts=true` from `.npmrc` to "fix" this — that re-opens the lifecycle-script attack surface for every other package in the tree. All CI workflows already invoke npm with `--ignore-scripts`, so local behaviour matches CI.
2041
+
2006
2042
  ## Resources
2007
2043
 
2008
2044
  - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)