got 14.6.5 → 15.0.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.
Files changed (35) hide show
  1. package/dist/source/as-promise/index.d.ts +2 -2
  2. package/dist/source/as-promise/index.js +59 -41
  3. package/dist/source/as-promise/types.d.ts +10 -23
  4. package/dist/source/as-promise/types.js +1 -17
  5. package/dist/source/core/calculate-retry-delay.js +1 -4
  6. package/dist/source/core/diagnostics-channel.js +12 -21
  7. package/dist/source/core/errors.d.ts +2 -1
  8. package/dist/source/core/errors.js +7 -10
  9. package/dist/source/core/index.d.ts +19 -7
  10. package/dist/source/core/index.js +726 -311
  11. package/dist/source/core/options.d.ts +92 -91
  12. package/dist/source/core/options.js +616 -303
  13. package/dist/source/core/response.d.ts +5 -3
  14. package/dist/source/core/response.js +26 -3
  15. package/dist/source/core/timed-out.d.ts +1 -1
  16. package/dist/source/core/timed-out.js +3 -3
  17. package/dist/source/core/utils/defer-to-connect.js +5 -17
  18. package/dist/source/core/utils/get-body-size.d.ts +1 -1
  19. package/dist/source/core/utils/get-body-size.js +3 -20
  20. package/dist/source/core/utils/proxy-events.d.ts +1 -1
  21. package/dist/source/core/utils/proxy-events.js +3 -3
  22. package/dist/source/core/utils/strip-url-auth.d.ts +1 -0
  23. package/dist/source/core/utils/strip-url-auth.js +9 -0
  24. package/dist/source/core/utils/timer.js +5 -7
  25. package/dist/source/core/utils/unhandle.js +1 -2
  26. package/dist/source/create.js +83 -27
  27. package/dist/source/index.d.ts +2 -3
  28. package/dist/source/index.js +0 -4
  29. package/dist/source/types.d.ts +42 -70
  30. package/package.json +34 -38
  31. package/readme.md +2 -2
  32. package/dist/source/core/utils/is-form-data.d.ts +0 -7
  33. package/dist/source/core/utils/is-form-data.js +0 -4
  34. package/dist/source/core/utils/url-to-options.d.ts +0 -14
  35. package/dist/source/core/utils/url-to-options.js +0 -22
@@ -1,3 +1,3 @@
1
1
  import Request from '../core/index.js';
2
- import { type CancelableRequest } from './types.js';
3
- export default function asPromise<T>(firstRequest?: Request): CancelableRequest<T>;
2
+ import { type RequestPromise } from './types.js';
3
+ export default function asPromise<T>(firstRequest?: Request): RequestPromise<T>;
@@ -1,11 +1,11 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import is from '@sindresorhus/is';
3
- import PCancelable from 'p-cancelable';
4
3
  import { HTTPError, RetryError, } from '../core/errors.js';
5
- import Request from '../core/index.js';
6
- import { parseBody, isResponseOk, ParseError, } from '../core/response.js';
4
+ import Request, { normalizeError } from '../core/index.js';
5
+ import { decodeUint8Array, isUtf8Encoding, parseBody, isResponseOk, ParseError, } from '../core/response.js';
7
6
  import proxyEvents from '../core/utils/proxy-events.js';
8
- import { CancelError } from './types.js';
7
+ import { applyUrlOverride, isSameOrigin, snapshotCrossOriginState, } from '../core/options.js';
8
+ const compressedEncodings = new Set(['gzip', 'deflate', 'br', 'zstd']);
9
9
  const proxiedRequestEvents = [
10
10
  'request',
11
11
  'response',
@@ -16,31 +16,18 @@ const proxiedRequestEvents = [
16
16
  export default function asPromise(firstRequest) {
17
17
  let globalRequest;
18
18
  let globalResponse;
19
- let normalizedOptions;
20
19
  const emitter = new EventEmitter();
21
20
  let promiseSettled = false;
22
- const promise = new PCancelable((resolve, reject, onCancel) => {
23
- onCancel(() => {
24
- globalRequest.destroy();
25
- });
26
- onCancel.shouldReject = false;
27
- onCancel(() => {
28
- promiseSettled = true;
29
- reject(new CancelError(globalRequest));
30
- });
31
- const makeRequest = (retryCount) => {
32
- // Errors when a new request is made after the promise settles.
33
- // Used to detect a race condition.
34
- // See https://github.com/sindresorhus/got/issues/1489
35
- onCancel(() => { });
36
- const request = firstRequest ?? new Request(undefined, undefined, normalizedOptions);
21
+ const promise = new Promise((resolve, reject) => {
22
+ const makeRequest = (retryCount, defaultOptions) => {
23
+ const request = firstRequest ?? new Request(undefined, undefined, defaultOptions);
37
24
  request.retryCount = retryCount;
38
25
  request._noPipe = true;
39
26
  globalRequest = request;
40
27
  request.once('response', async (response) => {
41
28
  // Parse body
42
29
  const contentEncoding = (response.headers['content-encoding'] ?? '').toLowerCase();
43
- const isCompressed = contentEncoding === 'gzip' || contentEncoding === 'deflate' || contentEncoding === 'br' || contentEncoding === 'zstd';
30
+ const isCompressed = compressedEncodings.has(contentEncoding);
44
31
  const { options } = request;
45
32
  if (isCompressed && !options.decompress) {
46
33
  response.body = response.rawBody;
@@ -52,14 +39,14 @@ export default function asPromise(firstRequest) {
52
39
  catch (error) {
53
40
  // Fall back to `utf8`
54
41
  try {
55
- response.body = response.rawBody.toString();
42
+ response.body = decodeUint8Array(response.rawBody);
56
43
  }
57
44
  catch (error) {
58
- request._beforeError(new ParseError(error, response));
45
+ request._beforeError(new ParseError(normalizeError(error), response));
59
46
  return;
60
47
  }
61
48
  if (isResponseOk(response)) {
62
- request._beforeError(error);
49
+ request._beforeError(normalizeError(error));
63
50
  return;
64
51
  }
65
52
  }
@@ -67,14 +54,41 @@ export default function asPromise(firstRequest) {
67
54
  try {
68
55
  const hooks = options.hooks.afterResponse;
69
56
  for (const [index, hook] of hooks.entries()) {
70
- // @ts-expect-error TS doesn't notice that CancelableRequest is a Promise
57
+ const previousUrl = options.url ? new URL(options.url) : undefined;
58
+ const previousState = previousUrl ? snapshotCrossOriginState(options) : undefined;
59
+ const requestOptions = response.request.options;
60
+ const responseSnapshot = response;
61
+ // @ts-expect-error TS doesn't notice that RequestPromise is a Promise
71
62
  // eslint-disable-next-line no-await-in-loop
72
- response = await hook(response, async (updatedOptions) => {
63
+ response = await requestOptions.trackStateMutations(async (changedState) => hook(responseSnapshot, async (updatedOptions) => {
73
64
  const preserveHooks = updatedOptions.preserveHooks ?? false;
74
- options.merge(updatedOptions);
75
- options.prefixUrl = '';
65
+ const reusesRequestOptions = updatedOptions === requestOptions;
66
+ const hasExplicitBody = reusesRequestOptions
67
+ ? changedState.has('body') || changedState.has('json') || changedState.has('form')
68
+ : (Object.hasOwn(updatedOptions, 'body') && updatedOptions.body !== undefined)
69
+ || (Object.hasOwn(updatedOptions, 'json') && updatedOptions.json !== undefined)
70
+ || (Object.hasOwn(updatedOptions, 'form') && updatedOptions.form !== undefined);
71
+ if (hasExplicitBody && !reusesRequestOptions) {
72
+ options.clearBody();
73
+ }
74
+ if (!reusesRequestOptions) {
75
+ options.merge(updatedOptions);
76
+ }
76
77
  if (updatedOptions.url) {
77
- options.url = updatedOptions.url;
78
+ const nextUrl = reusesRequestOptions
79
+ ? options.url
80
+ : applyUrlOverride(options, updatedOptions.url, updatedOptions);
81
+ if (previousUrl) {
82
+ if (reusesRequestOptions && !isSameOrigin(previousUrl, nextUrl)) {
83
+ options.stripUnchangedCrossOriginState(previousState, changedState, { clearBody: !hasExplicitBody });
84
+ }
85
+ else {
86
+ options.stripSensitiveHeaders(previousUrl, nextUrl, updatedOptions);
87
+ if (!isSameOrigin(previousUrl, nextUrl) && !hasExplicitBody) {
88
+ options.clearBody();
89
+ }
90
+ }
91
+ }
78
92
  }
79
93
  // Remove any further hooks for that request, because we'll call them anyway.
80
94
  // The loop continues. We don't want duplicates (asPromise recursion).
@@ -83,14 +97,14 @@ export default function asPromise(firstRequest) {
83
97
  options.hooks.afterResponse = options.hooks.afterResponse.slice(0, index);
84
98
  }
85
99
  throw new RetryError(request);
86
- });
100
+ }));
87
101
  if (!(is.object(response) && is.number(response.statusCode) && 'body' in response)) {
88
102
  throw new TypeError('The `afterResponse` hook returned an invalid value');
89
103
  }
90
104
  }
91
105
  }
92
106
  catch (error) {
93
- request._beforeError(error);
107
+ request._beforeError(normalizeError(error));
94
108
  return;
95
109
  }
96
110
  globalResponse = response;
@@ -104,9 +118,6 @@ export default function asPromise(firstRequest) {
104
118
  });
105
119
  let handledFinalError = false;
106
120
  const onError = (error) => {
107
- if (promise.isCanceled) {
108
- return;
109
- }
110
121
  // Route errors emitted directly on the stream (e.g., EPIPE from Node.js)
111
122
  // through retry logic first, then handle them here after retries are exhausted.
112
123
  // See https://github.com/sindresorhus/got/issues/1995
@@ -145,15 +156,14 @@ export default function asPromise(firstRequest) {
145
156
  return;
146
157
  }
147
158
  const newBody = request.options.body;
148
- if (previousBody === newBody && is.nodeStream(newBody)) {
159
+ if (previousBody === newBody && (is.nodeStream(newBody) || newBody instanceof ReadableStream)) {
149
160
  error.message = 'Cannot retry with consumed body stream';
150
161
  onError(error);
151
162
  return;
152
163
  }
153
164
  // This is needed! We need to reuse `request.options` because they can get modified!
154
165
  // For example, by calling `promise.json()`.
155
- normalizedOptions = request.options;
156
- makeRequest(newRetryCount);
166
+ makeRequest(newRetryCount, request.options);
157
167
  });
158
168
  proxyEvents(request, emitter, proxiedRequestEvents);
159
169
  if (is.undefined(firstRequest)) {
@@ -162,19 +172,27 @@ export default function asPromise(firstRequest) {
162
172
  };
163
173
  makeRequest(0);
164
174
  });
165
- promise.on = (event, function_) => {
175
+ promise.on = function (event, function_) {
166
176
  emitter.on(event, function_);
167
- return promise;
177
+ return this;
178
+ };
179
+ promise.once = function (event, function_) {
180
+ emitter.once(event, function_);
181
+ return this;
168
182
  };
169
- promise.off = (event, function_) => {
183
+ promise.off = function (event, function_) {
170
184
  emitter.off(event, function_);
171
- return promise;
185
+ return this;
172
186
  };
173
187
  const shortcut = (promiseToAwait, responseType) => {
174
188
  const newPromise = (async () => {
175
189
  // Wait until downloading has ended
176
190
  await promiseToAwait;
177
191
  const { options } = globalResponse.request;
192
+ if (responseType === 'text') {
193
+ const text = decodeUint8Array(globalResponse.rawBody, options.encoding);
194
+ return (isUtf8Encoding(options.encoding) ? text.replace(/^\uFEFF/u, '') : text);
195
+ }
178
196
  return parseBody(globalResponse, responseType, options.parseJson, options.encoding);
179
197
  })();
180
198
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
@@ -1,37 +1,24 @@
1
- import type { Buffer } from 'node:buffer';
2
- import type PCancelable from 'p-cancelable';
3
- import { RequestError } from '../core/errors.js';
4
- import type Request from '../core/index.js';
5
1
  import { type RequestEvents } from '../core/index.js';
6
2
  import type { Response } from '../core/response.js';
7
- /**
8
- An error to be thrown when the request is aborted with `.cancel()`.
9
- */
10
- export declare class CancelError extends RequestError {
11
- readonly response: Response;
12
- constructor(request: Request);
13
- /**
14
- Whether the promise is canceled.
15
- */
16
- get isCanceled(): boolean;
17
- }
18
- export interface CancelableRequest<T extends Response | Response['body'] = Response['body']> extends PCancelable<T>, RequestEvents<CancelableRequest<T>> {
3
+ interface RequestPromiseShape<T extends Response | Response['body'] = Response['body']> extends RequestEvents<RequestPromise<T>> {
19
4
  /**
20
5
  A shortcut method that gives a Promise returning a JSON object.
21
6
 
22
- It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'json'`.
7
+ It is semantically the same as setting `options.resolveBodyOnly` to `true` and `options.responseType` to `'json'`.
23
8
  */
24
- json: <ReturnType>() => CancelableRequest<ReturnType>;
9
+ json: <ReturnType>() => RequestPromise<ReturnType>;
25
10
  /**
26
- A shortcut method that gives a Promise returning a [Buffer](https://nodejs.org/api/buffer.html).
11
+ A shortcut method that gives a Promise returning a [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).
27
12
 
28
- It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'buffer'`.
13
+ It is semantically the same as setting `options.resolveBodyOnly` to `true` and `options.responseType` to `'buffer'`.
29
14
  */
30
- buffer: () => CancelableRequest<Buffer>;
15
+ buffer: () => RequestPromise<Uint8Array<ArrayBuffer>>;
31
16
  /**
32
17
  A shortcut method that gives a Promise returning a string.
33
18
 
34
- It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'text'`.
19
+ It is semantically the same as setting `options.resolveBodyOnly` to `true` and `options.responseType` to `'text'`.
35
20
  */
36
- text: () => CancelableRequest<string>;
21
+ text: () => RequestPromise<string>;
37
22
  }
23
+ export type RequestPromise<T extends Response | Response['body'] = Response['body']> = Promise<T> & RequestPromiseShape<T>;
24
+ export {};
@@ -1,17 +1 @@
1
- import { RequestError } from '../core/errors.js';
2
- /**
3
- An error to be thrown when the request is aborted with `.cancel()`.
4
- */
5
- export class CancelError extends RequestError {
6
- constructor(request) {
7
- super('Promise was canceled', {}, request);
8
- this.name = 'CancelError';
9
- this.code = 'ERR_CANCELED';
10
- }
11
- /**
12
- Whether the promise is canceled.
13
- */
14
- get isCanceled() {
15
- return true;
16
- }
17
- }
1
+ export {};
@@ -14,10 +14,7 @@ const calculateRetryDelay = ({ attemptCount, retryOptions, error, retryAfter, co
14
14
  if (error.response) {
15
15
  if (retryAfter) {
16
16
  // In this case `computedValue` is `options.request.timeout`
17
- if (retryAfter > computedValue) {
18
- return 0;
19
- }
20
- return retryAfter;
17
+ return retryAfter > computedValue ? 0 : retryAfter;
21
18
  }
22
19
  if (error.response.statusCode === 413) {
23
20
  return 0;
@@ -12,38 +12,29 @@ const channels = {
12
12
  export function generateRequestId() {
13
13
  return randomUUID();
14
14
  }
15
- export function publishRequestCreate(message) {
16
- if (channels.requestCreate.hasSubscribers) {
17
- channels.requestCreate.publish(message);
15
+ const publishToChannel = (channel, message) => {
16
+ if (channel.hasSubscribers) {
17
+ channel.publish(message);
18
18
  }
19
+ };
20
+ export function publishRequestCreate(message) {
21
+ publishToChannel(channels.requestCreate, message);
19
22
  }
20
23
  export function publishRequestStart(message) {
21
- if (channels.requestStart.hasSubscribers) {
22
- channels.requestStart.publish(message);
23
- }
24
+ publishToChannel(channels.requestStart, message);
24
25
  }
25
26
  export function publishResponseStart(message) {
26
- if (channels.responseStart.hasSubscribers) {
27
- channels.responseStart.publish(message);
28
- }
27
+ publishToChannel(channels.responseStart, message);
29
28
  }
30
29
  export function publishResponseEnd(message) {
31
- if (channels.responseEnd.hasSubscribers) {
32
- channels.responseEnd.publish(message);
33
- }
30
+ publishToChannel(channels.responseEnd, message);
34
31
  }
35
32
  export function publishRetry(message) {
36
- if (channels.retry.hasSubscribers) {
37
- channels.retry.publish(message);
38
- }
33
+ publishToChannel(channels.retry, message);
39
34
  }
40
35
  export function publishError(message) {
41
- if (channels.error.hasSubscribers) {
42
- channels.error.publish(message);
43
- }
36
+ publishToChannel(channels.error, message);
44
37
  }
45
38
  export function publishRedirect(message) {
46
- if (channels.redirect.hasSubscribers) {
47
- channels.redirect.publish(message);
48
- }
39
+ publishToChannel(channels.redirect, message);
49
40
  }
@@ -37,7 +37,7 @@ export declare class MaxRedirectsError extends RequestError {
37
37
  An error to be thrown when the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304.
38
38
  Includes a `response` property.
39
39
  */
40
- export declare class HTTPError<T = any> extends RequestError<T> {
40
+ export declare class HTTPError<T = unknown> extends RequestError<T> {
41
41
  name: string;
42
42
  code: string;
43
43
  readonly response: Response<T>;
@@ -78,6 +78,7 @@ An error to be thrown when reading from response stream fails.
78
78
  */
79
79
  export declare class ReadError extends RequestError {
80
80
  name: string;
81
+ code: string;
81
82
  readonly request: Request;
82
83
  readonly response: Response;
83
84
  readonly timings: Timings;
@@ -1,4 +1,5 @@
1
1
  import is from '@sindresorhus/is';
2
+ import stripUrlAuth from './utils/strip-url-auth.js';
2
3
  // A hacky check to prevent circular references.
3
4
  function isRequest(x) {
4
5
  return is.object(x) && '_onResponse' in x;
@@ -65,13 +66,12 @@ export class MaxRedirectsError extends RequestError {
65
66
  An error to be thrown when the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304.
66
67
  Includes a `response` property.
67
68
  */
68
- // TODO: Change `HTTPError<T = any>` to `HTTPError<T = unknown>` in the next major version to enforce type usage.
69
69
  // eslint-disable-next-line @typescript-eslint/naming-convention
70
70
  export class HTTPError extends RequestError {
71
71
  name = 'HTTPError';
72
72
  code = 'ERR_NON_2XX_3XX_RESPONSE';
73
73
  constructor(response) {
74
- super(`Request failed with status code ${response.statusCode} (${response.statusMessage}): ${response.request.options.method} ${response.request.options.url.toString()}`, {}, response.request);
74
+ super(`Request failed with status code ${response.statusCode} (${response.statusMessage}): ${response.request.options.method} ${stripUrlAuth(response.request.options.url)}`, {}, response.request);
75
75
  }
76
76
  }
77
77
  /**
@@ -82,9 +82,7 @@ export class CacheError extends RequestError {
82
82
  name = 'CacheError';
83
83
  constructor(error, request) {
84
84
  super(error.message, error, request);
85
- if (this.code === 'ERR_GOT_REQUEST_ERROR') {
86
- this.code = 'ERR_CACHE_ACCESS';
87
- }
85
+ this.code = 'ERR_CACHE_ACCESS';
88
86
  }
89
87
  }
90
88
  /**
@@ -94,9 +92,7 @@ export class UploadError extends RequestError {
94
92
  name = 'UploadError';
95
93
  constructor(error, request) {
96
94
  super(error.message, error, request);
97
- if (this.code === 'ERR_GOT_REQUEST_ERROR') {
98
- this.code = 'ERR_UPLOAD';
99
- }
95
+ this.code = 'ERR_UPLOAD';
100
96
  }
101
97
  }
102
98
  /**
@@ -118,10 +114,11 @@ An error to be thrown when reading from response stream fails.
118
114
  */
119
115
  export class ReadError extends RequestError {
120
116
  name = 'ReadError';
117
+ code = 'ERR_READING_RESPONSE_STREAM';
121
118
  constructor(error, request) {
122
119
  super(error.message, error, request);
123
- if (this.code === 'ERR_GOT_REQUEST_ERROR') {
124
- this.code = 'ERR_READING_RESPONSE_STREAM';
120
+ if (error.code === 'ECONNRESET' || error.code === 'ERR_HTTP_CONTENT_LENGTH_MISMATCH') {
121
+ this.code = error.code;
125
122
  }
126
123
  }
127
124
  }
@@ -77,6 +77,8 @@ export type RequestEvents<T> = {
77
77
  once: GotEventFunction<T>;
78
78
  off: GotEventFunction<T>;
79
79
  };
80
+ export { crossOriginStripHeaders } from './options.js';
81
+ export declare const normalizeError: (error: unknown) => Error;
80
82
  type UrlType = ConstructorParameters<typeof Options>[0];
81
83
  type OptionsType = ConstructorParameters<typeof Options>[1];
82
84
  type DefaultsType = ConstructorParameters<typeof Options>[2];
@@ -90,24 +92,24 @@ export default class Request extends Duplex implements RequestEvents<Request> {
90
92
  retryCount: number;
91
93
  _stopReading: boolean;
92
94
  private _requestOptions;
93
- private _stopRetry;
95
+ private _stopRetry?;
94
96
  private _downloadedSize;
95
97
  private _uploadedSize;
96
98
  private readonly _pipedServerResponses;
97
99
  private _request?;
98
100
  private _responseSize?;
99
101
  private _bodySize?;
100
- private _unproxyEvents;
101
- private _isFromCache?;
102
+ private _unproxyEvents?;
102
103
  private _triggerRead;
103
104
  private readonly _jobs;
104
- private _cancelTimeouts;
105
- private readonly _removeListeners;
106
- private _nativeResponse?;
105
+ private _cancelTimeouts?;
106
+ private readonly _abortListenerDisposer?;
107
107
  private _flushed;
108
108
  private _aborted;
109
109
  private _expectedContentLength?;
110
110
  private _compressedBytesCount?;
111
+ private _skipRequestEndInFinal;
112
+ private _incrementalDecode?;
111
113
  private readonly _requestId;
112
114
  private _requestInitialized;
113
115
  constructor(url: UrlType, options?: OptionsType, defaults?: DefaultsType);
@@ -121,14 +123,25 @@ export default class Request extends Duplex implements RequestEvents<Request> {
121
123
  end?: boolean;
122
124
  }): T;
123
125
  unpipe<T extends NodeJS.WritableStream>(destination: T): this;
126
+ private _shouldIncrementallyDecodeBody;
124
127
  private _checkContentLengthMismatch;
125
128
  private _finalizeBody;
126
129
  private _onResponseBase;
127
130
  private _setRawBody;
128
131
  private _onResponse;
129
132
  private _onRequest;
133
+ private _isRequestStale;
130
134
  private _asyncWrite;
131
135
  private _sendBody;
136
+ private _writeBodyInChunks;
137
+ private _finalizeStaleChunkedWrite;
138
+ private _emitUploadComplete;
139
+ private _endWritableRequest;
140
+ private _stripCrossOriginState;
141
+ private _stripUnchangedCrossOriginState;
142
+ private _dropBody;
143
+ private readonly _onBodyError;
144
+ private _writeChunksToRequest;
132
145
  private _prepareCache;
133
146
  private _createCacheableRequest;
134
147
  private _makeRequest;
@@ -189,4 +202,3 @@ export default class Request extends Duplex implements RequestEvents<Request> {
189
202
  */
190
203
  get isReadonly(): boolean;
191
204
  }
192
- export {};