got 12.0.0-beta.1 → 12.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.
- package/dist/source/as-promise/index.d.ts +1 -1
- package/dist/source/as-promise/index.js +5 -9
- package/dist/source/as-promise/types.d.ts +19 -0
- package/dist/source/as-promise/types.js +3 -0
- package/dist/source/core/errors.js +1 -0
- package/dist/source/core/index.d.ts +7 -8
- package/dist/source/core/index.js +45 -20
- package/dist/source/core/options.d.ts +196 -76
- package/dist/source/core/options.js +144 -98
- package/dist/source/core/response.d.ts +2 -0
- package/dist/source/core/response.js +4 -4
- package/dist/source/core/timed-out.d.ts +1 -1
- package/dist/source/core/timed-out.js +1 -1
- package/dist/source/core/utils/get-body-size.d.ts +1 -1
- package/dist/source/core/utils/get-body-size.js +2 -1
- package/dist/source/core/utils/is-client-request.d.ts +2 -2
- package/dist/source/core/utils/is-form-data.d.ts +1 -1
- package/dist/source/core/utils/options-to-url.d.ts +1 -1
- package/dist/source/core/utils/options-to-url.js +1 -1
- package/dist/source/core/utils/proxy-events.d.ts +2 -2
- package/dist/source/core/utils/unhandle.d.ts +1 -1
- package/dist/source/core/utils/url-to-options.d.ts +1 -1
- package/dist/source/create.js +2 -1
- package/dist/source/types.d.ts +4 -3
- package/package.json +32 -26
- package/readme.md +96 -76
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
2
|
import is from '@sindresorhus/is';
|
|
3
3
|
import PCancelable from 'p-cancelable';
|
|
4
4
|
import { HTTPError, RetryError, } from '../core/errors.js';
|
|
@@ -38,7 +38,7 @@ export default function asPromise(firstRequest) {
|
|
|
38
38
|
request.once('response', async (response) => {
|
|
39
39
|
// Parse body
|
|
40
40
|
const contentEncoding = (response.headers['content-encoding'] ?? '').toLowerCase();
|
|
41
|
-
const isCompressed = contentEncoding === 'gzip' || contentEncoding === '
|
|
41
|
+
const isCompressed = contentEncoding === 'gzip' || contentEncoding === 'deflate' || contentEncoding === 'br';
|
|
42
42
|
const { options } = request;
|
|
43
43
|
if (isCompressed && !options.decompress) {
|
|
44
44
|
response.body = response.rawBody;
|
|
@@ -48,7 +48,7 @@ export default function asPromise(firstRequest) {
|
|
|
48
48
|
response.body = parseBody(response, options.responseType, options.parseJson, options.encoding);
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
51
|
-
//
|
|
51
|
+
// Fall back to `utf8`
|
|
52
52
|
response.body = response.rawBody.toString();
|
|
53
53
|
if (isResponseOk(response)) {
|
|
54
54
|
request._beforeError(error);
|
|
@@ -58,10 +58,7 @@ export default function asPromise(firstRequest) {
|
|
|
58
58
|
}
|
|
59
59
|
try {
|
|
60
60
|
const hooks = options.hooks.afterResponse;
|
|
61
|
-
|
|
62
|
-
// eslint-disable-next-line unicorn/no-for-loop
|
|
63
|
-
for (let index = 0; index < hooks.length; index++) {
|
|
64
|
-
const hook = hooks[index];
|
|
61
|
+
for (const [index, hook] of hooks.entries()) {
|
|
65
62
|
// @ts-expect-error TS doesn't notice that CancelableRequest is a Promise
|
|
66
63
|
// eslint-disable-next-line no-await-in-loop
|
|
67
64
|
response = await hook(response, async (updatedOptions) => {
|
|
@@ -75,7 +72,7 @@ export default function asPromise(firstRequest) {
|
|
|
75
72
|
options.hooks.afterResponse = options.hooks.afterResponse.slice(0, index);
|
|
76
73
|
throw new RetryError(request);
|
|
77
74
|
});
|
|
78
|
-
if (!(is.object(response) && is.number(response.statusCode) && response.body)) {
|
|
75
|
+
if (!(is.object(response) && is.number(response.statusCode) && !is.nullOrUndefined(response.body))) {
|
|
79
76
|
throw new TypeError('The `afterResponse` hook returned an invalid value');
|
|
80
77
|
}
|
|
81
78
|
}
|
|
@@ -108,7 +105,6 @@ export default function asPromise(firstRequest) {
|
|
|
108
105
|
request.once('error', onError);
|
|
109
106
|
const previousBody = request.options?.body;
|
|
110
107
|
request.once('retry', (newRetryCount, error) => {
|
|
111
|
-
// @ts-expect-error
|
|
112
108
|
firstRequest = undefined;
|
|
113
109
|
const newBody = request.options.body;
|
|
114
110
|
if (previousBody === newBody && is.nodeStream(newBody)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
import type { Buffer } from 'node:buffer';
|
|
2
3
|
import PCancelable from 'p-cancelable';
|
|
3
4
|
import { RequestError } from '../core/errors.js';
|
|
4
5
|
import type Request from '../core/index.js';
|
|
@@ -10,10 +11,28 @@ An error to be thrown when the request is aborted with `.cancel()`.
|
|
|
10
11
|
export declare class CancelError extends RequestError {
|
|
11
12
|
readonly response: Response;
|
|
12
13
|
constructor(request: Request);
|
|
14
|
+
/**
|
|
15
|
+
Whether the promise is canceled.
|
|
16
|
+
*/
|
|
13
17
|
get isCanceled(): boolean;
|
|
14
18
|
}
|
|
15
19
|
export interface CancelableRequest<T extends Response | Response['body'] = Response['body']> extends PCancelable<T>, RequestEvents<CancelableRequest<T>> {
|
|
20
|
+
/**
|
|
21
|
+
A shortcut method that gives a Promise returning a JSON object.
|
|
22
|
+
|
|
23
|
+
It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'json'`.
|
|
24
|
+
*/
|
|
16
25
|
json: <ReturnType>() => CancelableRequest<ReturnType>;
|
|
26
|
+
/**
|
|
27
|
+
A shortcut method that gives a Promise returning a [Buffer](https://nodejs.org/api/buffer.html).
|
|
28
|
+
|
|
29
|
+
It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'buffer'`.
|
|
30
|
+
*/
|
|
17
31
|
buffer: () => CancelableRequest<Buffer>;
|
|
32
|
+
/**
|
|
33
|
+
A shortcut method that gives a Promise returning a string.
|
|
34
|
+
|
|
35
|
+
It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'text'`.
|
|
36
|
+
*/
|
|
18
37
|
text: () => CancelableRequest<string>;
|
|
19
38
|
}
|
|
@@ -93,6 +93,7 @@ export class MaxRedirectsError extends RequestError {
|
|
|
93
93
|
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.
|
|
94
94
|
Includes a `response` property.
|
|
95
95
|
*/
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
96
97
|
export class HTTPError extends RequestError {
|
|
97
98
|
constructor(response) {
|
|
98
99
|
super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { Duplex } from 'stream';
|
|
3
|
-
import { URL } from 'url';
|
|
4
|
-
import { ServerResponse } from 'http';
|
|
5
|
-
import type { ClientRequest } from 'http';
|
|
6
|
-
import type { Socket } from 'net';
|
|
2
|
+
import { Duplex } from 'node:stream';
|
|
3
|
+
import { URL } from 'node:url';
|
|
4
|
+
import { ServerResponse } from 'node:http';
|
|
5
|
+
import type { ClientRequest } from 'node:http';
|
|
6
|
+
import type { Socket } from 'node:net';
|
|
7
7
|
import CacheableRequest from 'cacheable-request';
|
|
8
8
|
import type { Timings } from '@szmarczak/http-timer';
|
|
9
9
|
import type ResponseLike from 'responselike';
|
|
@@ -12,12 +12,12 @@ import { Response } from './response.js';
|
|
|
12
12
|
import { RequestError } from './errors.js';
|
|
13
13
|
import type { PlainResponse } from './response.js';
|
|
14
14
|
import type { NativeRequestOptions } from './options.js';
|
|
15
|
+
declare type Error = NodeJS.ErrnoException;
|
|
15
16
|
export interface Progress {
|
|
16
17
|
percent: number;
|
|
17
18
|
transferred: number;
|
|
18
19
|
total?: number;
|
|
19
20
|
}
|
|
20
|
-
declare type Error = NodeJS.ErrnoException;
|
|
21
21
|
export declare type GotEventFunction<T> =
|
|
22
22
|
/**
|
|
23
23
|
`request` event to get the request object of the request.
|
|
@@ -96,7 +96,6 @@ export default class Request extends Duplex implements RequestEvents<Request> {
|
|
|
96
96
|
private _downloadedSize;
|
|
97
97
|
private _uploadedSize;
|
|
98
98
|
private _stopReading;
|
|
99
|
-
private _startedReading;
|
|
100
99
|
private readonly _pipedServerResponses;
|
|
101
100
|
private _request?;
|
|
102
101
|
private _responseSize?;
|
|
@@ -115,7 +114,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
|
|
|
115
114
|
flush(): Promise<void>;
|
|
116
115
|
_beforeError(error: Error): void;
|
|
117
116
|
_read(): void;
|
|
118
|
-
_write(chunk:
|
|
117
|
+
_write(chunk: unknown, encoding: BufferEncoding | undefined, callback: (error?: Error | null) => void): void;
|
|
119
118
|
_final(callback: (error?: Error | null) => void): void;
|
|
120
119
|
_destroy(error: Error | null, callback: (error: Error | null) => void): void;
|
|
121
120
|
pipe<T extends NodeJS.WritableStream>(destination: T, options?: {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
3
|
+
import { Duplex } from 'node:stream';
|
|
4
|
+
import { URL, URLSearchParams } from 'node:url';
|
|
5
|
+
import http, { ServerResponse } from 'node:http';
|
|
4
6
|
import timer from '@szmarczak/http-timer';
|
|
5
7
|
import CacheableRequest from 'cacheable-request';
|
|
6
8
|
import decompressResponse from 'decompress-response';
|
|
7
9
|
import is from '@sindresorhus/is';
|
|
8
10
|
import { buffer as getBuffer } from 'get-stream';
|
|
11
|
+
import { FormDataEncoder, isFormDataLike } from 'form-data-encoder';
|
|
9
12
|
import getBodySize from './utils/get-body-size.js';
|
|
10
13
|
import isFormData from './utils/is-form-data.js';
|
|
11
14
|
import proxyEvents from './utils/proxy-events.js';
|
|
@@ -104,12 +107,6 @@ export default class Request extends Duplex {
|
|
|
104
107
|
writable: true,
|
|
105
108
|
value: void 0
|
|
106
109
|
});
|
|
107
|
-
Object.defineProperty(this, "_startedReading", {
|
|
108
|
-
enumerable: true,
|
|
109
|
-
configurable: true,
|
|
110
|
-
writable: true,
|
|
111
|
-
value: void 0
|
|
112
|
-
});
|
|
113
110
|
Object.defineProperty(this, "_pipedServerResponses", {
|
|
114
111
|
enumerable: true,
|
|
115
112
|
configurable: true,
|
|
@@ -192,7 +189,6 @@ export default class Request extends Duplex {
|
|
|
192
189
|
this._downloadedSize = 0;
|
|
193
190
|
this._uploadedSize = 0;
|
|
194
191
|
this._stopReading = false;
|
|
195
|
-
this._startedReading = false;
|
|
196
192
|
this._pipedServerResponses = new Set();
|
|
197
193
|
this._cannotHaveBody = false;
|
|
198
194
|
this._unproxyEvents = noop;
|
|
@@ -228,6 +224,11 @@ export default class Request extends Duplex {
|
|
|
228
224
|
Object.assign(this.options.headers, source.headers);
|
|
229
225
|
}
|
|
230
226
|
});
|
|
227
|
+
this.on('newListener', event => {
|
|
228
|
+
if (event === 'retry' && this.listenerCount('retry') > 0) {
|
|
229
|
+
throw new Error('A retry listener has been attached already.');
|
|
230
|
+
}
|
|
231
|
+
});
|
|
231
232
|
try {
|
|
232
233
|
this.options = new Options(url, options, defaults);
|
|
233
234
|
if (!this.options.url) {
|
|
@@ -384,7 +385,14 @@ export default class Request extends Duplex {
|
|
|
384
385
|
return;
|
|
385
386
|
}
|
|
386
387
|
this.destroy();
|
|
387
|
-
this.emit('retry', this.retryCount + 1, error)
|
|
388
|
+
this.emit('retry', this.retryCount + 1, error, (updatedOptions) => {
|
|
389
|
+
const request = new Request(options.url, updatedOptions, options);
|
|
390
|
+
request.retryCount = this.retryCount + 1;
|
|
391
|
+
process.nextTick(() => {
|
|
392
|
+
void request.flush();
|
|
393
|
+
});
|
|
394
|
+
return request;
|
|
395
|
+
});
|
|
388
396
|
return;
|
|
389
397
|
}
|
|
390
398
|
}
|
|
@@ -403,7 +411,6 @@ export default class Request extends Duplex {
|
|
|
403
411
|
let data;
|
|
404
412
|
while ((data = response.read()) !== null) {
|
|
405
413
|
this._downloadedSize += data.length;
|
|
406
|
-
this._startedReading = true;
|
|
407
414
|
const progress = this.downloadProgress;
|
|
408
415
|
if (progress.percent < 1) {
|
|
409
416
|
this.emit('downloadProgress', progress);
|
|
@@ -412,7 +419,6 @@ export default class Request extends Duplex {
|
|
|
412
419
|
}
|
|
413
420
|
}
|
|
414
421
|
}
|
|
415
|
-
// Node.js 12 has incorrect types, so the encoding must be a string
|
|
416
422
|
_write(chunk, encoding, callback) {
|
|
417
423
|
const write = () => {
|
|
418
424
|
this._writeRequest(chunk, encoding, callback);
|
|
@@ -474,9 +480,6 @@ export default class Request extends Duplex {
|
|
|
474
480
|
callback(error);
|
|
475
481
|
}
|
|
476
482
|
pipe(destination, options) {
|
|
477
|
-
if (this._startedReading) {
|
|
478
|
-
throw new Error('Failed to pipe. The response has been emitted already.');
|
|
479
|
-
}
|
|
480
483
|
if (destination instanceof ServerResponse) {
|
|
481
484
|
this._pipedServerResponses.add(destination);
|
|
482
485
|
}
|
|
@@ -504,6 +507,7 @@ export default class Request extends Duplex {
|
|
|
504
507
|
const { options } = this;
|
|
505
508
|
const { headers } = options;
|
|
506
509
|
const isForm = !is.undefined(options.form);
|
|
510
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
507
511
|
const isJSON = !is.undefined(options.json);
|
|
508
512
|
const isBody = !is.undefined(options.body);
|
|
509
513
|
const cannotHaveBody = methodsWithoutBody.has(options.method) && !(options.method === 'GET' && options.allowGetBody);
|
|
@@ -515,6 +519,15 @@ export default class Request extends Duplex {
|
|
|
515
519
|
// Serialize body
|
|
516
520
|
const noContentType = !is.string(headers['content-type']);
|
|
517
521
|
if (isBody) {
|
|
522
|
+
// Body is spec-compliant FormData
|
|
523
|
+
if (isFormDataLike(options.body)) {
|
|
524
|
+
const encoder = new FormDataEncoder(options.body);
|
|
525
|
+
if (noContentType) {
|
|
526
|
+
headers['content-type'] = encoder.headers['Content-Type'];
|
|
527
|
+
}
|
|
528
|
+
headers['content-length'] = encoder.headers['Content-Length'];
|
|
529
|
+
options.body = encoder.encode();
|
|
530
|
+
}
|
|
518
531
|
// Special case for https://github.com/form-data/form-data
|
|
519
532
|
if (isFormData(options.body) && noContentType) {
|
|
520
533
|
headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`;
|
|
@@ -837,7 +850,7 @@ export default class Request extends Duplex {
|
|
|
837
850
|
if (is.promise(result)) {
|
|
838
851
|
// We only need to implement the error handler in order to support HTTP2 caching.
|
|
839
852
|
// The result will be a promise anyway.
|
|
840
|
-
// @ts-expect-error
|
|
853
|
+
// @ts-expect-error ignore
|
|
841
854
|
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
842
855
|
result.once = (event, handler) => {
|
|
843
856
|
if (event === 'error') {
|
|
@@ -901,7 +914,7 @@ export default class Request extends Duplex {
|
|
|
901
914
|
}
|
|
902
915
|
async _makeRequest() {
|
|
903
916
|
const { options } = this;
|
|
904
|
-
const { headers } = options;
|
|
917
|
+
const { headers, username, password } = options;
|
|
905
918
|
const cookieJar = options.cookieJar;
|
|
906
919
|
for (const key in headers) {
|
|
907
920
|
if (is.undefined(headers[key])) {
|
|
@@ -915,6 +928,10 @@ export default class Request extends Duplex {
|
|
|
915
928
|
if (options.decompress && is.undefined(headers['accept-encoding'])) {
|
|
916
929
|
headers['accept-encoding'] = supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate';
|
|
917
930
|
}
|
|
931
|
+
if (username || password) {
|
|
932
|
+
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
|
933
|
+
headers.authorization = `Basic ${credentials}`;
|
|
934
|
+
}
|
|
918
935
|
// Set cookies
|
|
919
936
|
if (cookieJar) {
|
|
920
937
|
const cookieString = await cookieJar.getCookieString(options.url.toString());
|
|
@@ -947,10 +964,18 @@ export default class Request extends Duplex {
|
|
|
947
964
|
// Cache support
|
|
948
965
|
const fn = options.cache ? this._createCacheableRequest : request;
|
|
949
966
|
try {
|
|
950
|
-
|
|
967
|
+
// We can't do `await fn(...)`,
|
|
968
|
+
// because stream `error` event can be emitted before `Promise.resolve()`.
|
|
969
|
+
let requestOrResponse = fn(url, this._requestOptions);
|
|
970
|
+
if (is.promise(requestOrResponse)) {
|
|
971
|
+
requestOrResponse = await requestOrResponse;
|
|
972
|
+
}
|
|
951
973
|
// Fallback
|
|
952
974
|
if (is.undefined(requestOrResponse)) {
|
|
953
|
-
requestOrResponse =
|
|
975
|
+
requestOrResponse = options.getFallbackRequestFunction()(url, this._requestOptions);
|
|
976
|
+
if (is.promise(requestOrResponse)) {
|
|
977
|
+
requestOrResponse = await requestOrResponse;
|
|
978
|
+
}
|
|
954
979
|
}
|
|
955
980
|
if (isClientRequest(requestOrResponse)) {
|
|
956
981
|
this._onRequest(requestOrResponse);
|