groove-dev 0.27.144 → 0.27.145

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 (100) hide show
  1. package/node_modules/@groove-dev/cli/package.json +1 -1
  2. package/node_modules/@groove-dev/daemon/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/src/conversations.js +18 -48
  4. package/node_modules/@groove-dev/daemon/src/routes/agents.js +6 -83
  5. package/node_modules/@groove-dev/gui/dist/assets/{index-BcoF6_eF.js → index-Bxc0gU06.js} +232 -238
  6. package/node_modules/@groove-dev/gui/dist/assets/index-C0pztKBn.css +1 -0
  7. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  8. package/node_modules/@groove-dev/gui/package.json +1 -1
  9. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +80 -95
  10. package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +2 -70
  11. package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +2 -0
  12. package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +68 -66
  13. package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +4 -8
  14. package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +39 -31
  15. package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +66 -65
  16. package/node_modules/@groove-dev/gui/src/components/lab/preset-manager.jsx +17 -14
  17. package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +126 -127
  18. package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +10 -8
  19. package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +8 -8
  20. package/node_modules/@groove-dev/gui/src/lib/status.js +1 -0
  21. package/node_modules/@groove-dev/gui/src/stores/groove.js +17 -0
  22. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +8 -1
  23. package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +13 -14
  24. package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +41 -10
  25. package/node_modules/@groove-dev/gui/src/views/models.jsx +57 -36
  26. package/node_modules/axios/CHANGELOG.md +260 -0
  27. package/node_modules/axios/README.md +595 -223
  28. package/node_modules/axios/dist/axios.js +1460 -1090
  29. package/node_modules/axios/dist/axios.js.map +1 -1
  30. package/node_modules/axios/dist/axios.min.js +3 -3
  31. package/node_modules/axios/dist/axios.min.js.map +1 -1
  32. package/node_modules/axios/dist/browser/axios.cjs +1560 -1132
  33. package/node_modules/axios/dist/browser/axios.cjs.map +1 -1
  34. package/node_modules/axios/dist/esm/axios.js +1557 -1128
  35. package/node_modules/axios/dist/esm/axios.js.map +1 -1
  36. package/node_modules/axios/dist/esm/axios.min.js +2 -2
  37. package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
  38. package/node_modules/axios/dist/node/axios.cjs +1594 -1057
  39. package/node_modules/axios/dist/node/axios.cjs.map +1 -1
  40. package/node_modules/axios/index.d.cts +40 -41
  41. package/node_modules/axios/index.d.ts +151 -227
  42. package/node_modules/axios/index.js +2 -0
  43. package/node_modules/axios/lib/adapters/adapters.js +4 -2
  44. package/node_modules/axios/lib/adapters/fetch.js +147 -16
  45. package/node_modules/axios/lib/adapters/http.js +306 -58
  46. package/node_modules/axios/lib/adapters/xhr.js +6 -2
  47. package/node_modules/axios/lib/core/Axios.js +7 -3
  48. package/node_modules/axios/lib/core/AxiosError.js +120 -34
  49. package/node_modules/axios/lib/core/AxiosHeaders.js +27 -25
  50. package/node_modules/axios/lib/core/buildFullPath.js +1 -1
  51. package/node_modules/axios/lib/core/dispatchRequest.js +19 -7
  52. package/node_modules/axios/lib/core/mergeConfig.js +21 -4
  53. package/node_modules/axios/lib/core/settle.js +7 -11
  54. package/node_modules/axios/lib/defaults/index.js +14 -9
  55. package/node_modules/axios/lib/env/data.js +1 -1
  56. package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +1 -2
  57. package/node_modules/axios/lib/helpers/buildURL.js +1 -1
  58. package/node_modules/axios/lib/helpers/cookies.js +14 -2
  59. package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +28 -1
  60. package/node_modules/axios/lib/helpers/formDataToJSON.js +3 -1
  61. package/node_modules/axios/lib/helpers/formDataToStream.js +3 -2
  62. package/node_modules/axios/lib/helpers/parseProtocol.js +1 -1
  63. package/node_modules/axios/lib/helpers/progressEventReducer.js +5 -5
  64. package/node_modules/axios/lib/helpers/resolveConfig.js +54 -18
  65. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +74 -2
  66. package/node_modules/axios/lib/helpers/toFormData.js +10 -2
  67. package/node_modules/axios/lib/helpers/validator.js +3 -1
  68. package/node_modules/axios/lib/utils.js +33 -21
  69. package/node_modules/axios/package.json +17 -24
  70. package/node_modules/follow-redirects/README.md +7 -5
  71. package/node_modules/follow-redirects/index.js +24 -1
  72. package/node_modules/follow-redirects/package.json +1 -1
  73. package/package.json +1 -1
  74. package/packages/cli/package.json +1 -1
  75. package/packages/daemon/package.json +1 -1
  76. package/packages/daemon/src/conversations.js +18 -48
  77. package/packages/daemon/src/routes/agents.js +6 -83
  78. package/packages/gui/dist/assets/{index-BcoF6_eF.js → index-Bxc0gU06.js} +232 -238
  79. package/packages/gui/dist/assets/index-C0pztKBn.css +1 -0
  80. package/packages/gui/dist/index.html +2 -2
  81. package/packages/gui/package.json +1 -1
  82. package/packages/gui/src/components/agents/agent-feed.jsx +80 -95
  83. package/packages/gui/src/components/agents/agent-panel.jsx +2 -70
  84. package/packages/gui/src/components/chat/chat-header.jsx +2 -0
  85. package/packages/gui/src/components/chat/chat-input.jsx +68 -66
  86. package/packages/gui/src/components/chat/chat-view.jsx +4 -8
  87. package/packages/gui/src/components/lab/chat-playground.jsx +39 -31
  88. package/packages/gui/src/components/lab/parameter-panel.jsx +66 -65
  89. package/packages/gui/src/components/lab/preset-manager.jsx +17 -14
  90. package/packages/gui/src/components/lab/runtime-config.jsx +126 -127
  91. package/packages/gui/src/components/lab/system-prompt-editor.jsx +10 -8
  92. package/packages/gui/src/components/ui/slider.jsx +8 -8
  93. package/packages/gui/src/lib/status.js +1 -0
  94. package/packages/gui/src/stores/groove.js +17 -0
  95. package/packages/gui/src/stores/slices/agents-slice.js +8 -1
  96. package/packages/gui/src/stores/slices/chat-slice.js +13 -14
  97. package/packages/gui/src/views/model-lab.jsx +41 -10
  98. package/packages/gui/src/views/models.jsx +57 -36
  99. package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.css +0 -1
  100. package/packages/gui/dist/assets/index-Dd7qhiEd.css +0 -1
@@ -7,6 +7,7 @@ import http from 'http';
7
7
  import https from 'https';
8
8
  import http2 from 'http2';
9
9
  import util from 'util';
10
+ import { resolve as resolvePath } from 'path';
10
11
  import followRedirects from 'follow-redirects';
11
12
  import zlib from 'zlib';
12
13
  import { VERSION } from '../env/data.js';
@@ -46,11 +47,46 @@ const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
46
47
  const { http: httpFollow, https: httpsFollow } = followRedirects;
47
48
 
48
49
  const isHttps = /https:?/;
50
+ const FORM_DATA_CONTENT_HEADERS = ['content-type', 'content-length'];
51
+
52
+ function setFormDataHeaders(headers, formHeaders, policy) {
53
+ if (policy !== 'content-only') {
54
+ headers.set(formHeaders);
55
+ return;
56
+ }
57
+
58
+ Object.entries(formHeaders).forEach(([key, val]) => {
59
+ if (FORM_DATA_CONTENT_HEADERS.includes(key.toLowerCase())) {
60
+ headers.set(key, val);
61
+ }
62
+ });
63
+ }
64
+
65
+ // Symbols used to bind a single 'error' listener to a pooled socket and track
66
+ // the request currently owning that socket across keep-alive reuse (issue #10780).
67
+ const kAxiosSocketListener = Symbol('axios.http.socketListener');
68
+ const kAxiosCurrentReq = Symbol('axios.http.currentReq');
49
69
 
50
70
  const supportedProtocols = platform.protocols.map((protocol) => {
51
71
  return protocol + ':';
52
72
  });
53
73
 
74
+ // Node's WHATWG URL parser returns `username` and `password` percent-encoded.
75
+ // Decode before composing the `auth` option so credentials such as
76
+ // `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the
77
+ // original value for malformed input so a bad encoding never throws.
78
+ const decodeURIComponentSafe = (value) => {
79
+ if (!utils.isString(value)) {
80
+ return value;
81
+ }
82
+
83
+ try {
84
+ return decodeURIComponent(value);
85
+ } catch (error) {
86
+ return value;
87
+ }
88
+ };
89
+
54
90
  const flushOnFinish = (stream, [throttled, flush]) => {
55
91
  stream.on('end', flush).on('error', flush);
56
92
 
@@ -170,12 +206,12 @@ const http2Sessions = new Http2Sessions();
170
206
  *
171
207
  * @returns {Object<string, any>}
172
208
  */
173
- function dispatchBeforeRedirect(options, responseDetails) {
209
+ function dispatchBeforeRedirect(options, responseDetails, requestDetails) {
174
210
  if (options.beforeRedirects.proxy) {
175
211
  options.beforeRedirects.proxy(options);
176
212
  }
177
213
  if (options.beforeRedirects.config) {
178
- options.beforeRedirects.config(options, responseDetails);
214
+ options.beforeRedirects.config(options, responseDetails, requestDetails);
179
215
  }
180
216
  }
181
217
 
@@ -188,7 +224,7 @@ function dispatchBeforeRedirect(options, responseDetails) {
188
224
  *
189
225
  * @returns {http.ClientRequestArgs}
190
226
  */
191
- function setProxy(options, configProxy, location) {
227
+ function setProxy(options, configProxy, location, isRedirect) {
192
228
  let proxy = configProxy;
193
229
  if (!proxy && proxy !== false) {
194
230
  const proxyUrl = getProxyForUrl(location);
@@ -198,43 +234,87 @@ function setProxy(options, configProxy, location) {
198
234
  }
199
235
  }
200
236
  }
237
+ // On redirect re-invocation, strip any stale Proxy-Authorization header carried
238
+ // over from the prior request (e.g. new target no longer uses a proxy, or uses
239
+ // a different proxy). Skip on the initial request so user-supplied headers are
240
+ // preserved. Header names are case-insensitive, so remove every case variant.
241
+ if (isRedirect && options.headers) {
242
+ for (const name of Object.keys(options.headers)) {
243
+ if (name.toLowerCase() === 'proxy-authorization') {
244
+ delete options.headers[name];
245
+ }
246
+ }
247
+ }
201
248
  if (proxy) {
249
+ // Read proxy fields without traversing the prototype chain. URL instances expose
250
+ // username/password/hostname/host/port/protocol via getters on URL.prototype (so
251
+ // direct reads are shielded), but plain object proxies — and the `auth` field
252
+ // (which URL does not expose) — must be guarded so a polluted Object.prototype
253
+ // (e.g. Object.prototype.auth = { username, password }) cannot inject
254
+ // attacker-controlled credentials into the Proxy-Authorization header or
255
+ // redirect proxying to an attacker-controlled host.
256
+ const isProxyURL = proxy instanceof URL;
257
+ const readProxyField = (key) =>
258
+ isProxyURL || utils.hasOwnProp(proxy, key) ? proxy[key] : undefined;
259
+
260
+ const proxyUsername = readProxyField('username');
261
+ const proxyPassword = readProxyField('password');
262
+ let proxyAuth = utils.hasOwnProp(proxy, 'auth') ? proxy.auth : undefined;
263
+
202
264
  // Basic proxy authorization
203
- if (proxy.username) {
204
- proxy.auth = (proxy.username || '') + ':' + (proxy.password || '');
265
+ if (proxyUsername) {
266
+ proxyAuth = (proxyUsername || '') + ':' + (proxyPassword || '');
205
267
  }
206
268
 
207
- if (proxy.auth) {
208
- // Support proxy auth object form
209
- const validProxyAuth = Boolean(proxy.auth.username || proxy.auth.password);
269
+ if (proxyAuth) {
270
+ // Support proxy auth object form. Read sub-fields via own-prop checks so a
271
+ // plain object inheriting from polluted Object.prototype cannot leak creds.
272
+ const authIsObject = typeof proxyAuth === 'object';
273
+ const authUsername =
274
+ authIsObject && utils.hasOwnProp(proxyAuth, 'username') ? proxyAuth.username : undefined;
275
+ const authPassword =
276
+ authIsObject && utils.hasOwnProp(proxyAuth, 'password') ? proxyAuth.password : undefined;
277
+ const validProxyAuth = Boolean(authUsername || authPassword);
210
278
 
211
279
  if (validProxyAuth) {
212
- proxy.auth = (proxy.auth.username || '') + ':' + (proxy.auth.password || '');
213
- } else if (typeof proxy.auth === 'object') {
280
+ proxyAuth = (authUsername || '') + ':' + (authPassword || '');
281
+ } else if (authIsObject) {
214
282
  throw new AxiosError('Invalid proxy authorization', AxiosError.ERR_BAD_OPTION, { proxy });
215
283
  }
216
284
 
217
- const base64 = Buffer.from(proxy.auth, 'utf8').toString('base64');
285
+ const base64 = Buffer.from(proxyAuth, 'utf8').toString('base64');
218
286
 
219
287
  options.headers['Proxy-Authorization'] = 'Basic ' + base64;
220
288
  }
221
289
 
222
- options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
223
- const proxyHost = proxy.hostname || proxy.host;
290
+ // Preserve a user-supplied Host header (case-insensitive) so callers can override
291
+ // the value forwarded to the proxy; otherwise default to the request URL's host.
292
+ let hasUserHostHeader = false;
293
+ for (const name of Object.keys(options.headers)) {
294
+ if (name.toLowerCase() === 'host') {
295
+ hasUserHostHeader = true;
296
+ break;
297
+ }
298
+ }
299
+ if (!hasUserHostHeader) {
300
+ options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
301
+ }
302
+ const proxyHost = readProxyField('hostname') || readProxyField('host');
224
303
  options.hostname = proxyHost;
225
304
  // Replace 'host' since options is not a URL object
226
305
  options.host = proxyHost;
227
- options.port = proxy.port;
306
+ options.port = readProxyField('port');
228
307
  options.path = location;
229
- if (proxy.protocol) {
230
- options.protocol = proxy.protocol.includes(':') ? proxy.protocol : `${proxy.protocol}:`;
308
+ const proxyProtocol = readProxyField('protocol');
309
+ if (proxyProtocol) {
310
+ options.protocol = proxyProtocol.includes(':') ? proxyProtocol : `${proxyProtocol}:`;
231
311
  }
232
312
  }
233
313
 
234
314
  options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) {
235
315
  // Configure proxy for redirected request, passing the original config proxy to apply
236
316
  // the exact same logic as if the redirected request was performed by axios directly.
237
- setProxy(redirectOptions, configProxy, redirectOptions.href);
317
+ setProxy(redirectOptions, configProxy, redirectOptions.href, true);
238
318
  };
239
319
  }
240
320
 
@@ -333,12 +413,20 @@ const http2Transport = {
333
413
  export default isHttpAdapterSupported &&
334
414
  function httpAdapter(config) {
335
415
  return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) {
336
- let { data, lookup, family, httpVersion = 1, http2Options } = config;
337
- const { responseType, responseEncoding } = config;
416
+ const own = (key) => (utils.hasOwnProp(config, key) ? config[key] : undefined);
417
+ let data = own('data');
418
+ let lookup = own('lookup');
419
+ let family = own('family');
420
+ let httpVersion = own('httpVersion');
421
+ if (httpVersion === undefined) httpVersion = 1;
422
+ let http2Options = own('http2Options');
423
+ const responseType = own('responseType');
424
+ const responseEncoding = own('responseEncoding');
338
425
  const method = config.method.toUpperCase();
339
426
  let isDone;
340
427
  let rejected = false;
341
428
  let req;
429
+ let connectPhaseTimer;
342
430
 
343
431
  httpVersion = +httpVersion;
344
432
 
@@ -383,9 +471,34 @@ export default isHttpAdapterSupported &&
383
471
  }
384
472
  }
385
473
 
474
+ function clearConnectPhaseTimer() {
475
+ if (connectPhaseTimer) {
476
+ clearTimeout(connectPhaseTimer);
477
+ connectPhaseTimer = null;
478
+ }
479
+ }
480
+
481
+ function createTimeoutError() {
482
+ let timeoutErrorMessage = config.timeout
483
+ ? 'timeout of ' + config.timeout + 'ms exceeded'
484
+ : 'timeout exceeded';
485
+ const transitional = config.transitional || transitionalDefaults;
486
+ if (config.timeoutErrorMessage) {
487
+ timeoutErrorMessage = config.timeoutErrorMessage;
488
+ }
489
+ return new AxiosError(
490
+ timeoutErrorMessage,
491
+ transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
492
+ config,
493
+ req
494
+ );
495
+ }
496
+
386
497
  abortEmitter.once('abort', reject);
387
498
 
388
499
  const onFinished = () => {
500
+ clearConnectPhaseTimer();
501
+
389
502
  if (config.cancelToken) {
390
503
  config.cancelToken.unsubscribe(abort);
391
504
  }
@@ -406,6 +519,7 @@ export default isHttpAdapterSupported &&
406
519
 
407
520
  onDone((response, isRejected) => {
408
521
  isDone = true;
522
+ clearConnectPhaseTimer();
409
523
 
410
524
  if (isRejected) {
411
525
  rejected = true;
@@ -520,8 +634,12 @@ export default isHttpAdapterSupported &&
520
634
  }
521
635
  );
522
636
  // support for https://www.npmjs.com/package/form-data api
523
- } else if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) {
524
- headers.set(data.getHeaders());
637
+ } else if (
638
+ utils.isFormData(data) &&
639
+ utils.isFunction(data.getHeaders) &&
640
+ data.getHeaders !== Object.prototype.getHeaders
641
+ ) {
642
+ setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy'));
525
643
 
526
644
  if (!headers.hasContentLength()) {
527
645
  try {
@@ -606,15 +724,16 @@ export default isHttpAdapterSupported &&
606
724
 
607
725
  // HTTP basic authentication
608
726
  let auth = undefined;
609
- if (config.auth) {
610
- const username = config.auth.username || '';
611
- const password = config.auth.password || '';
727
+ const configAuth = own('auth');
728
+ if (configAuth) {
729
+ const username = configAuth.username || '';
730
+ const password = configAuth.password || '';
612
731
  auth = username + ':' + password;
613
732
  }
614
733
 
615
734
  if (!auth && parsed.username) {
616
- const urlUsername = parsed.username;
617
- const urlPassword = parsed.password;
735
+ const urlUsername = decodeURIComponentSafe(parsed.username);
736
+ const urlPassword = decodeURIComponentSafe(parsed.password);
618
737
  auth = urlUsername + ':' + urlPassword;
619
738
  }
620
739
 
@@ -642,7 +761,9 @@ export default isHttpAdapterSupported &&
642
761
  false
643
762
  );
644
763
 
645
- const options = {
764
+ // Null-prototype to block prototype pollution gadgets on properties read
765
+ // directly by Node's http.request (e.g. insecureHTTPParser, lookup).
766
+ const options = Object.assign(Object.create(null), {
646
767
  path,
647
768
  method: method,
648
769
  headers: headers.toJSON(),
@@ -651,14 +772,41 @@ export default isHttpAdapterSupported &&
651
772
  protocol,
652
773
  family,
653
774
  beforeRedirect: dispatchBeforeRedirect,
654
- beforeRedirects: {},
775
+ beforeRedirects: Object.create(null),
655
776
  http2Options,
656
- };
777
+ });
657
778
 
658
779
  // cacheable-lookup integration hotfix
659
780
  !utils.isUndefined(lookup) && (options.lookup = lookup);
660
781
 
661
782
  if (config.socketPath) {
783
+ if (typeof config.socketPath !== 'string') {
784
+ return reject(
785
+ new AxiosError('socketPath must be a string', AxiosError.ERR_BAD_OPTION_VALUE, config)
786
+ );
787
+ }
788
+
789
+ if (config.allowedSocketPaths != null) {
790
+ const allowed = Array.isArray(config.allowedSocketPaths)
791
+ ? config.allowedSocketPaths
792
+ : [config.allowedSocketPaths];
793
+
794
+ const resolvedSocket = resolvePath(config.socketPath);
795
+ const isAllowed = allowed.some(
796
+ (entry) => typeof entry === 'string' && resolvePath(entry) === resolvedSocket
797
+ );
798
+
799
+ if (!isAllowed) {
800
+ return reject(
801
+ new AxiosError(
802
+ `socketPath "${config.socketPath}" is not permitted by allowedSocketPaths`,
803
+ AxiosError.ERR_BAD_OPTION_VALUE,
804
+ config
805
+ )
806
+ );
807
+ }
808
+ }
809
+
662
810
  options.socketPath = config.socketPath;
663
811
  } else {
664
812
  options.hostname = parsed.hostname.startsWith('[')
@@ -672,22 +820,26 @@ export default isHttpAdapterSupported &&
672
820
  );
673
821
  }
674
822
  let transport;
823
+ let isNativeTransport = false;
675
824
  const isHttpsRequest = isHttps.test(options.protocol);
676
825
  options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
677
826
 
678
827
  if (isHttp2) {
679
828
  transport = http2Transport;
680
829
  } else {
681
- if (config.transport) {
682
- transport = config.transport;
830
+ const configTransport = own('transport');
831
+ if (configTransport) {
832
+ transport = configTransport;
683
833
  } else if (config.maxRedirects === 0) {
684
834
  transport = isHttpsRequest ? https : http;
835
+ isNativeTransport = true;
685
836
  } else {
686
837
  if (config.maxRedirects) {
687
838
  options.maxRedirects = config.maxRedirects;
688
839
  }
689
- if (config.beforeRedirect) {
690
- options.beforeRedirects.config = config.beforeRedirect;
840
+ const configBeforeRedirect = own('beforeRedirect');
841
+ if (configBeforeRedirect) {
842
+ options.beforeRedirects.config = configBeforeRedirect;
691
843
  }
692
844
  transport = isHttpsRequest ? httpsFollow : httpFollow;
693
845
  }
@@ -700,12 +852,15 @@ export default isHttpAdapterSupported &&
700
852
  options.maxBodyLength = Infinity;
701
853
  }
702
854
 
703
- if (config.insecureHTTPParser) {
704
- options.insecureHTTPParser = config.insecureHTTPParser;
705
- }
855
+ // Always set an explicit own value so a polluted
856
+ // Object.prototype.insecureHTTPParser cannot enable the lenient parser
857
+ // through Node's internal options copy
858
+ options.insecureHTTPParser = Boolean(own('insecureHTTPParser'));
706
859
 
707
860
  // Create the request
708
861
  req = transport.request(options, function handleResponse(res) {
862
+ clearConnectPhaseTimer();
863
+
709
864
  if (req.destroyed) return;
710
865
 
711
866
  const streams = [res];
@@ -786,6 +941,30 @@ export default isHttpAdapterSupported &&
786
941
  };
787
942
 
788
943
  if (responseType === 'stream') {
944
+ // Enforce maxContentLength on streamed responses; previously this
945
+ // was applied only to buffered responses.
946
+ if (config.maxContentLength > -1) {
947
+ const limit = config.maxContentLength;
948
+ const source = responseStream;
949
+ async function* enforceMaxContentLength() {
950
+ let totalResponseBytes = 0;
951
+ for await (const chunk of source) {
952
+ totalResponseBytes += chunk.length;
953
+ if (totalResponseBytes > limit) {
954
+ throw new AxiosError(
955
+ 'maxContentLength size of ' + limit + ' exceeded',
956
+ AxiosError.ERR_BAD_RESPONSE,
957
+ config,
958
+ lastRequest
959
+ );
960
+ }
961
+ yield chunk;
962
+ }
963
+ }
964
+ responseStream = stream.Readable.from(enforceMaxContentLength(), {
965
+ objectMode: false,
966
+ });
967
+ }
789
968
  response.data = responseStream;
790
969
  settle(resolve, reject, response);
791
970
  } else {
@@ -821,15 +1000,16 @@ export default isHttpAdapterSupported &&
821
1000
  'stream has been aborted',
822
1001
  AxiosError.ERR_BAD_RESPONSE,
823
1002
  config,
824
- lastRequest
1003
+ lastRequest,
1004
+ response
825
1005
  );
826
1006
  responseStream.destroy(err);
827
1007
  reject(err);
828
1008
  });
829
1009
 
830
1010
  responseStream.on('error', function handleStreamError(err) {
831
- if (req.destroyed) return;
832
- reject(AxiosError.from(err, null, config, lastRequest));
1011
+ if (rejected) return;
1012
+ reject(AxiosError.from(err, null, config, lastRequest, response));
833
1013
  });
834
1014
 
835
1015
  responseStream.on('end', function handleStreamEnd() {
@@ -872,9 +1052,48 @@ export default isHttpAdapterSupported &&
872
1052
  });
873
1053
 
874
1054
  // set tcp keep alive to prevent drop connection by peer
1055
+ // Track every socket bound to this outer RedirectableRequest so a single
1056
+ // 'close' listener can release ownership on all of them. follow-redirects
1057
+ // re-emits the 'socket' event for each hop's native request onto the same
1058
+ // outer request, so attaching per-request listeners inside this handler
1059
+ // would accumulate across hops and trigger MaxListenersExceededWarning at
1060
+ // >= 11 redirects. Clearing only the last-bound socket would leave stale
1061
+ // kAxiosCurrentReq refs on earlier hop sockets returned to the keep-alive
1062
+ // pool, causing an idle-pool 'error' to be attributed to a closed req.
1063
+ const boundSockets = new Set();
1064
+
875
1065
  req.on('socket', function handleRequestSocket(socket) {
876
1066
  // default interval of sending ack packet is 1 minute
877
1067
  socket.setKeepAlive(true, 1000 * 60);
1068
+
1069
+ // Install a single 'error' listener per socket (not per request) to avoid
1070
+ // accumulating listeners on pooled keep-alive sockets that get reassigned
1071
+ // to new requests before the previous request's 'close' fires (issue #10780).
1072
+ // The listener is bound to the socket's currently-active request via a
1073
+ // symbol, which is swapped as the socket is reassigned.
1074
+ if (!socket[kAxiosSocketListener]) {
1075
+ socket.on('error', function handleSocketError(err) {
1076
+ const current = socket[kAxiosCurrentReq];
1077
+ if (current && !current.destroyed) {
1078
+ current.destroy(err);
1079
+ }
1080
+ });
1081
+ socket[kAxiosSocketListener] = true;
1082
+ }
1083
+
1084
+ socket[kAxiosCurrentReq] = req;
1085
+ boundSockets.add(socket);
1086
+ });
1087
+
1088
+ req.once('close', function clearCurrentReq() {
1089
+ clearConnectPhaseTimer();
1090
+
1091
+ for (const socket of boundSockets) {
1092
+ if (socket[kAxiosCurrentReq] === req) {
1093
+ socket[kAxiosCurrentReq] = null;
1094
+ }
1095
+ }
1096
+ boundSockets.clear();
878
1097
  });
879
1098
 
880
1099
  // Handle request timeout
@@ -895,29 +1114,24 @@ export default isHttpAdapterSupported &&
895
1114
  return;
896
1115
  }
897
1116
 
1117
+ const handleTimeout = function handleTimeout() {
1118
+ if (isDone) return;
1119
+ abort(createTimeoutError());
1120
+ };
1121
+
1122
+ if (isNativeTransport && timeout > 0) {
1123
+ // Native ClientRequest#setTimeout starts from the socket lifecycle and
1124
+ // may not fire while TCP connect is still pending. Mirror the
1125
+ // follow-redirects wall-clock timer for the maxRedirects === 0 path.
1126
+ connectPhaseTimer = setTimeout(handleTimeout, timeout);
1127
+ }
1128
+
898
1129
  // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system.
899
1130
  // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET.
900
1131
  // At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up.
901
1132
  // And then these socket which be hang up will devouring CPU little by little.
902
1133
  // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect.
903
- req.setTimeout(timeout, function handleRequestTimeout() {
904
- if (isDone) return;
905
- let timeoutErrorMessage = config.timeout
906
- ? 'timeout of ' + config.timeout + 'ms exceeded'
907
- : 'timeout exceeded';
908
- const transitional = config.transitional || transitionalDefaults;
909
- if (config.timeoutErrorMessage) {
910
- timeoutErrorMessage = config.timeoutErrorMessage;
911
- }
912
- abort(
913
- new AxiosError(
914
- timeoutErrorMessage,
915
- transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
916
- config,
917
- req
918
- )
919
- );
920
- });
1134
+ req.setTimeout(timeout, handleTimeout);
921
1135
  } else {
922
1136
  // explicitly reset the socket timeout value for a possible `keep-alive` request
923
1137
  req.setTimeout(0);
@@ -943,7 +1157,41 @@ export default isHttpAdapterSupported &&
943
1157
  }
944
1158
  });
945
1159
 
946
- data.pipe(req);
1160
+ // Enforce maxBodyLength for streamed uploads on the native http/https
1161
+ // transport (maxRedirects === 0); follow-redirects enforces it on the
1162
+ // other path.
1163
+ let uploadStream = data;
1164
+ if (config.maxBodyLength > -1 && config.maxRedirects === 0) {
1165
+ const limit = config.maxBodyLength;
1166
+ let bytesSent = 0;
1167
+ uploadStream = stream.pipeline(
1168
+ [
1169
+ data,
1170
+ new stream.Transform({
1171
+ transform(chunk, _enc, cb) {
1172
+ bytesSent += chunk.length;
1173
+ if (bytesSent > limit) {
1174
+ return cb(
1175
+ new AxiosError(
1176
+ 'Request body larger than maxBodyLength limit',
1177
+ AxiosError.ERR_BAD_REQUEST,
1178
+ config,
1179
+ req
1180
+ )
1181
+ );
1182
+ }
1183
+ cb(null, chunk);
1184
+ },
1185
+ }),
1186
+ ],
1187
+ utils.noop
1188
+ );
1189
+ uploadStream.on('error', (err) => {
1190
+ if (!req.destroyed) req.destroy(err);
1191
+ });
1192
+ }
1193
+
1194
+ uploadStream.pipe(req);
947
1195
  } else {
948
1196
  data && req.write(data);
949
1197
  req.end();
@@ -91,7 +91,7 @@ export default isXHRAdapterSupported &&
91
91
  // will return status as 0 even though it's a successful request
92
92
  if (
93
93
  request.status === 0 &&
94
- !(request.responseURL && request.responseURL.indexOf('file:') === 0)
94
+ !(request.responseURL && request.responseURL.startsWith('file:'))
95
95
  ) {
96
96
  return;
97
97
  }
@@ -108,6 +108,7 @@ export default isXHRAdapterSupported &&
108
108
  }
109
109
 
110
110
  reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
111
+ done();
111
112
 
112
113
  // Clean up request
113
114
  request = null;
@@ -123,6 +124,7 @@ export default isXHRAdapterSupported &&
123
124
  // attach the underlying event for consumers who want details
124
125
  err.event = event || null;
125
126
  reject(err);
127
+ done();
126
128
  request = null;
127
129
  };
128
130
 
@@ -143,6 +145,7 @@ export default isXHRAdapterSupported &&
143
145
  request
144
146
  )
145
147
  );
148
+ done();
146
149
 
147
150
  // Clean up request
148
151
  request = null;
@@ -192,6 +195,7 @@ export default isXHRAdapterSupported &&
192
195
  }
193
196
  reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
194
197
  request.abort();
198
+ done();
195
199
  request = null;
196
200
  };
197
201
 
@@ -205,7 +209,7 @@ export default isXHRAdapterSupported &&
205
209
 
206
210
  const protocol = parseProtocol(_config.url);
207
211
 
208
- if (protocol && platform.protocols.indexOf(protocol) === -1) {
212
+ if (protocol && !platform.protocols.includes(protocol)) {
209
213
  reject(
210
214
  new AxiosError(
211
215
  'Unsupported protocol ' + protocol + ':',
@@ -148,7 +148,7 @@ class Axios {
148
148
  let contextHeaders = headers && utils.merge(headers.common, headers[config.method]);
149
149
 
150
150
  headers &&
151
- utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], (method) => {
151
+ utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch', 'query', 'common'], (method) => {
152
152
  delete headers[method];
153
153
  });
154
154
 
@@ -251,7 +251,7 @@ utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData
251
251
  };
252
252
  });
253
253
 
254
- utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
254
+ utils.forEach(['post', 'put', 'patch', 'query'], function forEachMethodWithData(method) {
255
255
  function generateHTTPMethod(isForm) {
256
256
  return function httpMethod(url, data, config) {
257
257
  return this.request(
@@ -271,7 +271,11 @@ utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
271
271
 
272
272
  Axios.prototype[method] = generateHTTPMethod();
273
273
 
274
- Axios.prototype[method + 'Form'] = generateHTTPMethod(true);
274
+ // QUERY is a safe/idempotent read method; multipart form bodies don't fit
275
+ // its semantics, so no queryForm shorthand is generated.
276
+ if (method !== 'query') {
277
+ Axios.prototype[method + 'Form'] = generateHTTPMethod(true);
278
+ }
275
279
  });
276
280
 
277
281
  export default Axios;