Comparing version 11.8.2 to 13.0.0
@@ -1,3 +0,3 @@ | ||
import { NormalizedOptions, CancelableRequest } from './types'; | ||
export default function asPromise<T>(normalizedOptions: NormalizedOptions): CancelableRequest<T>; | ||
export * from './types'; | ||
import Request from '../core/index.js'; | ||
import { type CancelableRequest } from './types.js'; | ||
export default function asPromise<T>(firstRequest?: Request): CancelableRequest<T>; |
@@ -1,22 +0,9 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const events_1 = require("events"); | ||
const is_1 = require("@sindresorhus/is"); | ||
const PCancelable = require("p-cancelable"); | ||
const types_1 = require("./types"); | ||
const parse_body_1 = require("./parse-body"); | ||
const core_1 = require("../core"); | ||
const proxy_events_1 = require("../core/utils/proxy-events"); | ||
const get_buffer_1 = require("../core/utils/get-buffer"); | ||
const is_response_ok_1 = require("../core/utils/is-response-ok"); | ||
import { EventEmitter } from 'node:events'; | ||
import is from '@sindresorhus/is'; | ||
import PCancelable from 'p-cancelable'; | ||
import { HTTPError, RetryError, } from '../core/errors.js'; | ||
import Request from '../core/index.js'; | ||
import { parseBody, isResponseOk } from '../core/response.js'; | ||
import proxyEvents from '../core/utils/proxy-events.js'; | ||
import { CancelError } from './types.js'; | ||
const proxiedRequestEvents = [ | ||
@@ -27,53 +14,42 @@ 'request', | ||
'uploadProgress', | ||
'downloadProgress' | ||
'downloadProgress', | ||
]; | ||
function asPromise(normalizedOptions) { | ||
export default function asPromise(firstRequest) { | ||
let globalRequest; | ||
let globalResponse; | ||
const emitter = new events_1.EventEmitter(); | ||
let normalizedOptions; | ||
const emitter = new EventEmitter(); | ||
const promise = new PCancelable((resolve, reject, onCancel) => { | ||
onCancel(() => { | ||
globalRequest.destroy(); | ||
}); | ||
onCancel.shouldReject = false; | ||
onCancel(() => { | ||
reject(new CancelError(globalRequest)); | ||
}); | ||
const makeRequest = (retryCount) => { | ||
const request = new core_1.default(undefined, normalizedOptions); | ||
// Errors when a new request is made after the promise settles. | ||
// Used to detect a race condition. | ||
// See https://github.com/sindresorhus/got/issues/1489 | ||
onCancel(() => { }); | ||
const request = firstRequest ?? new Request(undefined, undefined, normalizedOptions); | ||
request.retryCount = retryCount; | ||
request._noPipe = true; | ||
onCancel(() => request.destroy()); | ||
onCancel.shouldReject = false; | ||
onCancel(() => reject(new types_1.CancelError(request))); | ||
globalRequest = request; | ||
request.once('response', async (response) => { | ||
var _a; | ||
response.retryCount = retryCount; | ||
if (response.request.aborted) { | ||
// Canceled while downloading - will throw a `CancelError` or `TimeoutError` error | ||
return; | ||
} | ||
// Download body | ||
let rawBody; | ||
try { | ||
rawBody = await get_buffer_1.default(request); | ||
response.rawBody = rawBody; | ||
} | ||
catch (_b) { | ||
// The same error is caught below. | ||
// See request.once('error') | ||
return; | ||
} | ||
if (request._isAboutToError) { | ||
return; | ||
} | ||
// Parse body | ||
const contentEncoding = ((_a = response.headers['content-encoding']) !== null && _a !== void 0 ? _a : '').toLowerCase(); | ||
const isCompressed = ['gzip', 'deflate', 'br'].includes(contentEncoding); | ||
const contentEncoding = (response.headers['content-encoding'] ?? '').toLowerCase(); | ||
const isCompressed = contentEncoding === 'gzip' || contentEncoding === 'deflate' || contentEncoding === 'br'; | ||
const { options } = request; | ||
if (isCompressed && !options.decompress) { | ||
response.body = rawBody; | ||
response.body = response.rawBody; | ||
} | ||
else { | ||
try { | ||
response.body = parse_body_1.default(response, options.responseType, options.parseJson, options.encoding); | ||
response.body = parseBody(response, options.responseType, options.parseJson, options.encoding); | ||
} | ||
catch (error) { | ||
// Fallback to `utf8` | ||
response.body = rawBody.toString(); | ||
if (is_response_ok_1.isResponseOk(response)) { | ||
// Fall back to `utf8` | ||
response.body = response.rawBody.toString(); | ||
if (isResponseOk(response)) { | ||
request._beforeError(error); | ||
@@ -85,39 +61,32 @@ return; | ||
try { | ||
for (const [index, hook] of options.hooks.afterResponse.entries()) { | ||
const hooks = options.hooks.afterResponse; | ||
for (const [index, hook] of hooks.entries()) { | ||
// @ts-expect-error TS doesn't notice that CancelableRequest is a Promise | ||
// eslint-disable-next-line no-await-in-loop | ||
response = await hook(response, async (updatedOptions) => { | ||
const typedOptions = core_1.default.normalizeArguments(undefined, { | ||
...updatedOptions, | ||
retry: { | ||
calculateDelay: () => 0 | ||
}, | ||
throwHttpErrors: false, | ||
resolveBodyOnly: false | ||
}, options); | ||
options.merge(updatedOptions); | ||
options.prefixUrl = ''; | ||
if (updatedOptions.url) { | ||
options.url = updatedOptions.url; | ||
} | ||
// Remove any further hooks for that request, because we'll call them anyway. | ||
// The loop continues. We don't want duplicates (asPromise recursion). | ||
typedOptions.hooks.afterResponse = typedOptions.hooks.afterResponse.slice(0, index); | ||
for (const hook of typedOptions.hooks.beforeRetry) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await hook(typedOptions); | ||
} | ||
const promise = asPromise(typedOptions); | ||
onCancel(() => { | ||
promise.catch(() => { }); | ||
promise.cancel(); | ||
}); | ||
return promise; | ||
options.hooks.afterResponse = options.hooks.afterResponse.slice(0, index); | ||
throw new RetryError(request); | ||
}); | ||
if (!(is.object(response) && is.number(response.statusCode) && !is.nullOrUndefined(response.body))) { | ||
throw new TypeError('The `afterResponse` hook returned an invalid value'); | ||
} | ||
} | ||
} | ||
catch (error) { | ||
request._beforeError(new types_1.RequestError(error.message, error, request)); | ||
request._beforeError(error); | ||
return; | ||
} | ||
if (!is_response_ok_1.isResponseOk(response)) { | ||
request._beforeError(new types_1.HTTPError(response)); | ||
globalResponse = response; | ||
if (!isResponseOk(response)) { | ||
request._beforeError(new HTTPError(response)); | ||
return; | ||
} | ||
globalResponse = response; | ||
request.destroy(); | ||
resolve(request.options.resolveBodyOnly ? response.body : response); | ||
@@ -130,4 +99,5 @@ }); | ||
const { options } = request; | ||
if (error instanceof types_1.HTTPError && !options.throwHttpErrors) { | ||
if (error instanceof HTTPError && !options.throwHttpErrors) { | ||
const { response } = error; | ||
request.destroy(); | ||
resolve(request.options.resolveBodyOnly ? response.body : response); | ||
@@ -139,12 +109,20 @@ return; | ||
request.once('error', onError); | ||
const previousBody = request.options.body; | ||
const previousBody = request.options?.body; | ||
request.once('retry', (newRetryCount, error) => { | ||
var _a, _b; | ||
if (previousBody === ((_a = error.request) === null || _a === void 0 ? void 0 : _a.options.body) && is_1.default.nodeStream((_b = error.request) === null || _b === void 0 ? void 0 : _b.options.body)) { | ||
firstRequest = undefined; | ||
const newBody = request.options.body; | ||
if (previousBody === newBody && is.nodeStream(newBody)) { | ||
error.message = 'Cannot retry with consumed body stream'; | ||
onError(error); | ||
return; | ||
} | ||
// This is needed! We need to reuse `request.options` because they can get modified! | ||
// For example, by calling `promise.json()`. | ||
normalizedOptions = request.options; | ||
makeRequest(newRetryCount); | ||
}); | ||
proxy_events_1.default(request, emitter, proxiedRequestEvents); | ||
proxyEvents(request, emitter, proxiedRequestEvents); | ||
if (is.undefined(firstRequest)) { | ||
void request.flush(); | ||
} | ||
}; | ||
@@ -157,2 +135,6 @@ makeRequest(0); | ||
}; | ||
promise.off = (event, fn) => { | ||
emitter.off(event, fn); | ||
return promise; | ||
}; | ||
const shortcut = (responseType) => { | ||
@@ -163,4 +145,5 @@ const newPromise = (async () => { | ||
const { options } = globalResponse.request; | ||
return parse_body_1.default(globalResponse, responseType, options.parseJson, options.encoding); | ||
return parseBody(globalResponse, responseType, options.parseJson, options.encoding); | ||
})(); | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise)); | ||
@@ -170,5 +153,7 @@ return newPromise; | ||
promise.json = () => { | ||
const { headers } = globalRequest.options; | ||
if (!globalRequest.writableFinished && headers.accept === undefined) { | ||
headers.accept = 'application/json'; | ||
if (globalRequest.options) { | ||
const { headers } = globalRequest.options; | ||
if (!globalRequest.writableFinished && !('accept' in headers)) { | ||
headers.accept = 'application/json'; | ||
} | ||
} | ||
@@ -181,3 +166,1 @@ return shortcut('json'); | ||
} | ||
exports.default = asPromise; | ||
__exportStar(require("./types"), exports); |
@@ -1,244 +0,9 @@ | ||
/// <reference types="node" /> | ||
import PCancelable = require('p-cancelable'); | ||
import Request, { Options, Response, RequestError, RequestEvents } from '../core'; | ||
/// <reference types="node" resolution-mode="require"/> | ||
import type { Buffer } from 'node:buffer'; | ||
import type PCancelable from 'p-cancelable'; | ||
import { RequestError } from '../core/errors.js'; | ||
import type Request from '../core/index.js'; | ||
import type { RequestEvents } from '../core/index.js'; | ||
import type { Response } from '../core/response.js'; | ||
/** | ||
All parsing methods supported by Got. | ||
*/ | ||
export declare type ResponseType = 'json' | 'buffer' | 'text'; | ||
export interface PaginationOptions<T, R> { | ||
/** | ||
All options accepted by `got.paginate()`. | ||
*/ | ||
pagination?: { | ||
/** | ||
A function that transform [`Response`](#response) into an array of items. | ||
This is where you should do the parsing. | ||
@default response => JSON.parse(response.body) | ||
*/ | ||
transform?: (response: Response<R>) => Promise<T[]> | T[]; | ||
/** | ||
Checks whether the item should be emitted or not. | ||
@default (item, allItems, currentItems) => true | ||
*/ | ||
filter?: (item: T, allItems: T[], currentItems: T[]) => boolean; | ||
/** | ||
The function takes three arguments: | ||
- `response` - The current response object. | ||
- `allItems` - An array of the emitted items. | ||
- `currentItems` - Items from the current response. | ||
It should return an object representing Got options pointing to the next page. | ||
The options are merged automatically with the previous request, therefore the options returned `pagination.paginate(...)` must reflect changes only. | ||
If there are no more pages, `false` should be returned. | ||
@example | ||
``` | ||
const got = require('got'); | ||
(async () => { | ||
const limit = 10; | ||
const items = got.paginate('https://example.com/items', { | ||
searchParams: { | ||
limit, | ||
offset: 0 | ||
}, | ||
pagination: { | ||
paginate: (response, allItems, currentItems) => { | ||
const previousSearchParams = response.request.options.searchParams; | ||
const previousOffset = previousSearchParams.get('offset'); | ||
if (currentItems.length < limit) { | ||
return false; | ||
} | ||
return { | ||
searchParams: { | ||
...previousSearchParams, | ||
offset: Number(previousOffset) + limit, | ||
} | ||
}; | ||
} | ||
} | ||
}); | ||
console.log('Items from all pages:', items); | ||
})(); | ||
``` | ||
*/ | ||
paginate?: (response: Response<R>, allItems: T[], currentItems: T[]) => Options | false; | ||
/** | ||
Checks whether the pagination should continue. | ||
For example, if you need to stop **before** emitting an entry with some flag, you should use `(item, allItems, currentItems) => !item.flag`. | ||
If you want to stop **after** emitting the entry, you should use `(item, allItems, currentItems) => allItems.some(entry => entry.flag)` instead. | ||
@default (item, allItems, currentItems) => true | ||
*/ | ||
shouldContinue?: (item: T, allItems: T[], currentItems: T[]) => boolean; | ||
/** | ||
The maximum amount of items that should be emitted. | ||
@default Infinity | ||
*/ | ||
countLimit?: number; | ||
/** | ||
Milliseconds to wait before the next request is triggered. | ||
@default 0 | ||
*/ | ||
backoff?: number; | ||
/** | ||
The maximum amount of request that should be triggered. | ||
Retries on failure are not counted towards this limit. | ||
For example, it can be helpful during development to avoid an infinite number of requests. | ||
@default 10000 | ||
*/ | ||
requestLimit?: number; | ||
/** | ||
Defines how the parameter `allItems` in pagination.paginate, pagination.filter and pagination.shouldContinue is managed. | ||
When set to `false`, the parameter `allItems` is always an empty array. | ||
This option can be helpful to save on memory usage when working with a large dataset. | ||
*/ | ||
stackAllItems?: boolean; | ||
}; | ||
} | ||
export declare type AfterResponseHook = (response: Response, retryWithMergedOptions: (options: Options) => CancelableRequest<Response>) => Response | CancelableRequest<Response> | Promise<Response | CancelableRequest<Response>>; | ||
export declare namespace PromiseOnly { | ||
interface Hooks { | ||
/** | ||
Called with [response object](#response) and a retry function. | ||
Calling the retry function will trigger `beforeRetry` hooks. | ||
Each function should return the response. | ||
This is especially useful when you want to refresh an access token. | ||
__Note__: When using streams, this hook is ignored. | ||
@example | ||
``` | ||
const got = require('got'); | ||
const instance = got.extend({ | ||
hooks: { | ||
afterResponse: [ | ||
(response, retryWithMergedOptions) => { | ||
if (response.statusCode === 401) { // Unauthorized | ||
const updatedOptions = { | ||
headers: { | ||
token: getNewToken() // Refresh the access token | ||
} | ||
}; | ||
// Save for further requests | ||
instance.defaults.options = got.mergeOptions(instance.defaults.options, updatedOptions); | ||
// Make a new retry | ||
return retryWithMergedOptions(updatedOptions); | ||
} | ||
// No changes otherwise | ||
return response; | ||
} | ||
], | ||
beforeRetry: [ | ||
(options, error, retryCount) => { | ||
// This will be called on `retryWithMergedOptions(...)` | ||
} | ||
] | ||
}, | ||
mutableDefaults: true | ||
}); | ||
``` | ||
*/ | ||
afterResponse?: AfterResponseHook[]; | ||
} | ||
interface Options extends PaginationOptions<unknown, unknown> { | ||
/** | ||
The parsing method. | ||
The promise also has `.text()`, `.json()` and `.buffer()` methods which return another Got promise for the parsed body. | ||
It's like setting the options to `{responseType: 'json', resolveBodyOnly: true}` but without affecting the main Got promise. | ||
__Note__: When using streams, this option is ignored. | ||
@example | ||
``` | ||
(async () => { | ||
const responsePromise = got(url); | ||
const bufferPromise = responsePromise.buffer(); | ||
const jsonPromise = responsePromise.json(); | ||
const [response, buffer, json] = Promise.all([responsePromise, bufferPromise, jsonPromise]); | ||
// `response` is an instance of Got Response | ||
// `buffer` is an instance of Buffer | ||
// `json` is an object | ||
})(); | ||
``` | ||
@example | ||
``` | ||
// This | ||
const body = await got(url).json(); | ||
// is semantically the same as this | ||
const body = await got(url, {responseType: 'json', resolveBodyOnly: true}); | ||
``` | ||
*/ | ||
responseType?: ResponseType; | ||
/** | ||
When set to `true` the promise will return the Response body instead of the Response object. | ||
@default false | ||
*/ | ||
resolveBodyOnly?: boolean; | ||
/** | ||
Returns a `Stream` instead of a `Promise`. | ||
This is equivalent to calling `got.stream(url, options?)`. | ||
@default false | ||
*/ | ||
isStream?: boolean; | ||
/** | ||
[Encoding](https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings) to be used on `setEncoding` of the response data. | ||
To get a [`Buffer`](https://nodejs.org/api/buffer.html), you need to set `responseType` to `buffer` instead. | ||
Don't set this option to `null`. | ||
__Note__: This doesn't affect streams! Instead, you need to do `got.stream(...).setEncoding(encoding)`. | ||
@default 'utf-8' | ||
*/ | ||
encoding?: BufferEncoding; | ||
} | ||
interface NormalizedOptions { | ||
responseType: ResponseType; | ||
resolveBodyOnly: boolean; | ||
isStream: boolean; | ||
encoding?: BufferEncoding; | ||
pagination?: Required<PaginationOptions<unknown, unknown>['pagination']>; | ||
} | ||
interface Defaults { | ||
responseType: ResponseType; | ||
resolveBodyOnly: boolean; | ||
isStream: boolean; | ||
pagination?: Required<PaginationOptions<unknown, unknown>['pagination']>; | ||
} | ||
type HookEvent = 'afterResponse'; | ||
} | ||
/** | ||
An error to be thrown when server response code is 2xx, and parsing body fails. | ||
Includes a `response` property. | ||
*/ | ||
export declare class ParseError extends RequestError { | ||
readonly response: Response; | ||
constructor(error: Error, response: Response); | ||
} | ||
/** | ||
An error to be thrown when the request is aborted with `.cancel()`. | ||
@@ -249,9 +14,26 @@ */ | ||
constructor(request: Request); | ||
/** | ||
Whether the promise is canceled. | ||
*/ | ||
get isCanceled(): boolean; | ||
} | ||
export interface CancelableRequest<T extends Response | Response['body'] = Response['body']> extends PCancelable<T>, RequestEvents<CancelableRequest<T>> { | ||
/** | ||
A shortcut method that gives a Promise returning a JSON object. | ||
It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'json'`. | ||
*/ | ||
json: <ReturnType>() => CancelableRequest<ReturnType>; | ||
/** | ||
A shortcut method that gives a Promise returning a [Buffer](https://nodejs.org/api/buffer.html). | ||
It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'buffer'`. | ||
*/ | ||
buffer: () => CancelableRequest<Buffer>; | ||
/** | ||
A shortcut method that gives a Promise returning a string. | ||
It is semantically the same as settings `options.resolveBodyOnly` to `true` and `options.responseType` to `'text'`. | ||
*/ | ||
text: () => CancelableRequest<string>; | ||
} | ||
export * from '../core'; |
@@ -1,35 +0,14 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CancelError = exports.ParseError = void 0; | ||
const core_1 = require("../core"); | ||
import { RequestError } from '../core/errors.js'; | ||
/** | ||
An error to be thrown when server response code is 2xx, and parsing body fails. | ||
Includes a `response` property. | ||
*/ | ||
class ParseError extends core_1.RequestError { | ||
constructor(error, response) { | ||
const { options } = response.request; | ||
super(`${error.message} in "${options.url.toString()}"`, error, response.request); | ||
this.name = 'ParseError'; | ||
} | ||
} | ||
exports.ParseError = ParseError; | ||
/** | ||
An error to be thrown when the request is aborted with `.cancel()`. | ||
*/ | ||
class CancelError extends core_1.RequestError { | ||
export class CancelError extends RequestError { | ||
constructor(request) { | ||
super('Promise was canceled', {}, request); | ||
this.name = 'CancelError'; | ||
this.code = 'ERR_CANCELED'; | ||
} | ||
/** | ||
Whether the promise is canceled. | ||
*/ | ||
get isCanceled() { | ||
@@ -39,3 +18,1 @@ return true; | ||
} | ||
exports.CancelError = CancelError; | ||
__exportStar(require("../core"), exports); |
@@ -1,5 +0,4 @@ | ||
import { RetryFunction } from '.'; | ||
declare type Returns<T extends (...args: any) => unknown, V> = (...args: Parameters<T>) => V; | ||
export declare const retryAfterStatusCodes: ReadonlySet<number>; | ||
import type { RetryFunction } from './options.js'; | ||
type Returns<T extends (...args: any) => unknown, V> = (...args: Parameters<T>) => V; | ||
declare const calculateRetryDelay: Returns<RetryFunction, number>; | ||
export default calculateRetryDelay; |
@@ -1,6 +0,5 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.retryAfterStatusCodes = void 0; | ||
exports.retryAfterStatusCodes = new Set([413, 429, 503]); | ||
const calculateRetryDelay = ({ attemptCount, retryOptions, error, retryAfter }) => { | ||
const calculateRetryDelay = ({ attemptCount, retryOptions, error, retryAfter, computedValue, }) => { | ||
if (error.name === 'RetryError') { | ||
return 1; | ||
} | ||
if (attemptCount > retryOptions.limit) { | ||
@@ -17,3 +16,4 @@ return 0; | ||
if (retryAfter) { | ||
if (retryOptions.maxRetryAfter === undefined || retryAfter > retryOptions.maxRetryAfter) { | ||
// In this case `computedValue` is `options.request.timeout` | ||
if (retryAfter > computedValue) { | ||
return 0; | ||
@@ -27,5 +27,5 @@ } | ||
} | ||
const noise = Math.random() * 100; | ||
return ((2 ** (attemptCount - 1)) * 1000) + noise; | ||
const noise = Math.random() * retryOptions.noise; | ||
return Math.min(((2 ** (attemptCount - 1)) * 1000), retryOptions.backoffLimit) + noise; | ||
}; | ||
exports.default = calculateRetryDelay; | ||
export default calculateRetryDelay; |
@@ -1,1070 +0,139 @@ | ||
/// <reference types="node" /> | ||
import { Duplex, Readable } from 'stream'; | ||
import { URL, URLSearchParams } from 'url'; | ||
import { Socket } from 'net'; | ||
import { SecureContextOptions, DetailedPeerCertificate } from 'tls'; | ||
import http = require('http'); | ||
import { ClientRequest, RequestOptions, ServerResponse, request as httpRequest } from 'http'; | ||
import https = require('https'); | ||
import { Timings, IncomingMessageWithTimings } from '@szmarczak/http-timer'; | ||
import CacheableLookup from 'cacheable-lookup'; | ||
import CacheableRequest = require('cacheable-request'); | ||
import ResponseLike = require('responselike'); | ||
import { Delays, TimeoutError as TimedOutTimeoutError } from './utils/timed-out'; | ||
import { URLOptions } from './utils/options-to-url'; | ||
import { DnsLookupIpVersion } from './utils/dns-ip-version'; | ||
import { PromiseOnly } from '../as-promise/types'; | ||
declare type HttpRequestFunction = typeof httpRequest; | ||
declare type Error = NodeJS.ErrnoException; | ||
declare const kRequest: unique symbol; | ||
declare const kResponse: unique symbol; | ||
declare const kResponseSize: unique symbol; | ||
declare const kDownloadedSize: unique symbol; | ||
declare const kBodySize: unique symbol; | ||
declare const kUploadedSize: unique symbol; | ||
declare const kServerResponsesPiped: unique symbol; | ||
declare const kUnproxyEvents: unique symbol; | ||
declare const kIsFromCache: unique symbol; | ||
declare const kCancelTimeouts: unique symbol; | ||
declare const kStartedReading: unique symbol; | ||
declare const kStopReading: unique symbol; | ||
declare const kTriggerRead: unique symbol; | ||
declare const kBody: unique symbol; | ||
declare const kJobs: unique symbol; | ||
declare const kOriginalResponse: unique symbol; | ||
declare const kRetryTimeout: unique symbol; | ||
export declare const kIsNormalizedAlready: unique symbol; | ||
export interface Agents { | ||
http?: http.Agent; | ||
https?: https.Agent; | ||
http2?: unknown; | ||
} | ||
export declare const withoutBody: ReadonlySet<string>; | ||
export interface ToughCookieJar { | ||
getCookieString: ((currentUrl: string, options: Record<string, unknown>, cb: (err: Error | null, cookies: string) => void) => void) & ((url: string, callback: (error: Error | null, cookieHeader: string) => void) => void); | ||
setCookie: ((cookieOrString: unknown, currentUrl: string, options: Record<string, unknown>, cb: (err: Error | null, cookie: unknown) => void) => void) & ((rawCookie: string, url: string, callback: (error: Error | null, result: unknown) => void) => void); | ||
} | ||
export interface PromiseCookieJar { | ||
getCookieString: (url: string) => Promise<string>; | ||
setCookie: (rawCookie: string, url: string) => Promise<unknown>; | ||
} | ||
/// <reference types="node" resolution-mode="require"/> | ||
/// <reference types="node" resolution-mode="require"/> | ||
/// <reference types="node" resolution-mode="require"/> | ||
/// <reference types="node" resolution-mode="require"/> | ||
/// <reference types="node" resolution-mode="require"/> | ||
import { Duplex } from 'node:stream'; | ||
import type { ClientRequest } from 'node:http'; | ||
import type { Socket } from 'node:net'; | ||
import type { Timings } from '@szmarczak/http-timer'; | ||
import Options from './options.js'; | ||
import { type PlainResponse, type Response } from './response.js'; | ||
import { RequestError } from './errors.js'; | ||
type Error = NodeJS.ErrnoException; | ||
export type Progress = { | ||
percent: number; | ||
transferred: number; | ||
total?: number; | ||
}; | ||
export type GotEventFunction<T> = | ||
/** | ||
All available HTTP request methods provided by Got. | ||
*/ | ||
export declare type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'TRACE' | 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete' | 'options' | 'trace'; | ||
declare type Promisable<T> = T | Promise<T>; | ||
export declare type InitHook = (options: Options) => void; | ||
export declare type BeforeRequestHook = (options: NormalizedOptions) => Promisable<void | Response | ResponseLike>; | ||
export declare type BeforeRedirectHook = (options: NormalizedOptions, response: Response) => Promisable<void>; | ||
export declare type BeforeErrorHook = (error: RequestError) => Promisable<RequestError>; | ||
export declare type BeforeRetryHook = (options: NormalizedOptions, error?: RequestError, retryCount?: number) => void | Promise<void>; | ||
interface PlainHooks { | ||
/** | ||
Called with plain request options, right before their normalization. | ||
This is especially useful in conjunction with `got.extend()` when the input needs custom handling. | ||
`request` event to get the request object of the request. | ||
__Note #1__: This hook must be synchronous! | ||
__Tip__: You can use `request` event to abort requests. | ||
__Note #2__: Errors in this hook will be converted into an instances of `RequestError`. | ||
@example | ||
``` | ||
import got from 'got'; | ||
__Note #3__: The options object may not have a `url` property. | ||
To modify it, use a `beforeRequest` hook instead. | ||
@default [] | ||
*/ | ||
init?: InitHook[]; | ||
/** | ||
Called with normalized request options. | ||
Got will make no further changes to the request before it is sent. | ||
This is especially useful in conjunction with `got.extend()` when you want to create an API client that, for example, uses HMAC-signing. | ||
@default [] | ||
*/ | ||
beforeRequest?: BeforeRequestHook[]; | ||
/** | ||
Called with normalized request options and the redirect response. | ||
Got will make no further changes to the request. | ||
This is especially useful when you want to avoid dead sites. | ||
@default [] | ||
@example | ||
``` | ||
const got = require('got'); | ||
got('https://example.com', { | ||
hooks: { | ||
beforeRedirect: [ | ||
(options, response) => { | ||
if (options.hostname === 'deadSite') { | ||
options.hostname = 'fallbackSite'; | ||
} | ||
} | ||
] | ||
} | ||
}); | ||
``` | ||
*/ | ||
beforeRedirect?: BeforeRedirectHook[]; | ||
/** | ||
Called with an `Error` instance. | ||
The error is passed to the hook right before it's thrown. | ||
This is especially useful when you want to have more detailed errors. | ||
__Note__: Errors thrown while normalizing input options are thrown directly and not part of this hook. | ||
@default [] | ||
@example | ||
``` | ||
const got = require('got'); | ||
got('https://api.github.com/some-endpoint', { | ||
hooks: { | ||
beforeError: [ | ||
error => { | ||
const {response} = error; | ||
if (response && response.body) { | ||
error.name = 'GitHubError'; | ||
error.message = `${response.body.message} (${response.statusCode})`; | ||
} | ||
return error; | ||
} | ||
] | ||
} | ||
}); | ||
``` | ||
*/ | ||
beforeError?: BeforeErrorHook[]; | ||
/** | ||
Called with normalized request options, the error and the retry count. | ||
Got will make no further changes to the request. | ||
This is especially useful when some extra work is required before the next try. | ||
__Note__: When using streams, this hook is ignored. | ||
__Note__: When retrying in a `afterResponse` hook, all remaining `beforeRetry` hooks will be called without the `error` and `retryCount` arguments. | ||
@default [] | ||
@example | ||
``` | ||
const got = require('got'); | ||
got.post('https://example.com', { | ||
hooks: { | ||
beforeRetry: [ | ||
(options, error, retryCount) => { | ||
if (error.response.statusCode === 413) { // Payload too large | ||
options.body = getNewBody(); | ||
} | ||
} | ||
] | ||
} | ||
}); | ||
``` | ||
*/ | ||
beforeRetry?: BeforeRetryHook[]; | ||
} | ||
got.stream('https://github.com') | ||
.on('request', request => setTimeout(() => request.destroy(), 50)); | ||
``` | ||
*/ | ||
((name: 'request', listener: (request: ClientRequest) => void) => T) | ||
/** | ||
All available hook of Got. | ||
The `response` event to get the response object of the final request. | ||
*/ | ||
export interface Hooks extends PromiseOnly.Hooks, PlainHooks { | ||
} | ||
declare type PlainHookEvent = 'init' | 'beforeRequest' | 'beforeRedirect' | 'beforeError' | 'beforeRetry'; | ||
& (<R extends Response>(name: 'response', listener: (response: R) => void) => T) | ||
/** | ||
All hook events acceptable by Got. | ||
The `redirect` event to get the response object of a redirect. The second argument is options for the next request to the redirect location. | ||
*/ | ||
export declare type HookEvent = PromiseOnly.HookEvent | PlainHookEvent; | ||
export declare const knownHookEvents: HookEvent[]; | ||
declare type AcceptableResponse = IncomingMessageWithTimings | ResponseLike; | ||
declare type AcceptableRequestResult = AcceptableResponse | ClientRequest | Promise<AcceptableResponse | ClientRequest> | undefined; | ||
export declare type RequestFunction = (url: URL, options: RequestOptions, callback?: (response: AcceptableResponse) => void) => AcceptableRequestResult; | ||
export declare type Headers = Record<string, string | string[] | undefined>; | ||
declare type CheckServerIdentityFunction = (hostname: string, certificate: DetailedPeerCertificate) => Error | void; | ||
export declare type ParseJsonFunction = (text: string) => unknown; | ||
export declare type StringifyJsonFunction = (object: unknown) => string; | ||
export interface RetryObject { | ||
attemptCount: number; | ||
retryOptions: RequiredRetryOptions; | ||
error: TimeoutError | RequestError; | ||
computedValue: number; | ||
retryAfter?: number; | ||
} | ||
export declare type RetryFunction = (retryObject: RetryObject) => number | Promise<number>; | ||
& (<R extends Response, N extends Options>(name: 'redirect', listener: (response: R, nextOptions: N) => void) => T) | ||
/** | ||
An object representing `limit`, `calculateDelay`, `methods`, `statusCodes`, `maxRetryAfter` and `errorCodes` fields for maximum retry count, retry handler, allowed methods, allowed status codes, maximum [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time and allowed error codes. | ||
Progress events for uploading (sending a request) and downloading (receiving a response). | ||
The `progress` argument is an object like: | ||
Delays between retries counts with function `1000 * Math.pow(2, retry) + Math.random() * 100`, where `retry` is attempt number (starts from 1). | ||
The `calculateDelay` property is a `function` that receives an object with `attemptCount`, `retryOptions`, `error` and `computedValue` properties for current retry count, the retry options, error and default computed value. | ||
The function must return a delay in milliseconds (or a Promise resolving with it) (`0` return value cancels retry). | ||
By default, it retries *only* on the specified methods, status codes, and on these network errors: | ||
- `ETIMEDOUT`: One of the [timeout](#timeout) limits were reached. | ||
- `ECONNRESET`: Connection was forcibly closed by a peer. | ||
- `EADDRINUSE`: Could not bind to any free port. | ||
- `ECONNREFUSED`: Connection was refused by the server. | ||
- `EPIPE`: The remote side of the stream being written has been closed. | ||
- `ENOTFOUND`: Couldn't resolve the hostname to an IP address. | ||
- `ENETUNREACH`: No internet connection. | ||
- `EAI_AGAIN`: DNS lookup timed out. | ||
__Note__: If `maxRetryAfter` is set to `undefined`, it will use `options.timeout`. | ||
__Note__: If [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header is greater than `maxRetryAfter`, it will cancel the request. | ||
*/ | ||
export interface RequiredRetryOptions { | ||
limit: number; | ||
methods: Method[]; | ||
statusCodes: number[]; | ||
errorCodes: string[]; | ||
calculateDelay: RetryFunction; | ||
maxRetryAfter?: number; | ||
``` | ||
{ | ||
percent: 0.1, | ||
transferred: 1024, | ||
total: 10240 | ||
} | ||
export interface CacheOptions { | ||
shared?: boolean; | ||
cacheHeuristic?: number; | ||
immutableMinTimeToLive?: number; | ||
ignoreCargoCult?: boolean; | ||
} | ||
interface PlainOptions extends URLOptions { | ||
/** | ||
Custom request function. | ||
The main purpose of this is to [support HTTP2 using a wrapper](https://github.com/szmarczak/http2-wrapper). | ||
``` | ||
@default http.request | https.request | ||
*/ | ||
request?: RequestFunction; | ||
/** | ||
An object representing `http`, `https` and `http2` keys for [`http.Agent`](https://nodejs.org/api/http.html#http_class_http_agent), [`https.Agent`](https://nodejs.org/api/https.html#https_class_https_agent) and [`http2wrapper.Agent`](https://github.com/szmarczak/http2-wrapper#new-http2agentoptions) instance. | ||
This is necessary because a request to one protocol might redirect to another. | ||
In such a scenario, Got will switch over to the right protocol agent for you. | ||
If the `content-length` header is missing, `total` will be `undefined`. | ||
If a key is not present, it will default to a global agent. | ||
@example | ||
``` | ||
import got from 'got'; | ||
@example | ||
``` | ||
const got = require('got'); | ||
const HttpAgent = require('agentkeepalive'); | ||
const {HttpsAgent} = HttpAgent; | ||
got('https://sindresorhus.com', { | ||
agent: { | ||
http: new HttpAgent(), | ||
https: new HttpsAgent() | ||
} | ||
const response = await got('https://sindresorhus.com') | ||
.on('downloadProgress', progress => { | ||
// Report download progress | ||
}) | ||
.on('uploadProgress', progress => { | ||
// Report upload progress | ||
}); | ||
``` | ||
*/ | ||
agent?: Agents | false; | ||
/** | ||
Decompress the response automatically. | ||
This will set the `accept-encoding` header to `gzip, deflate, br` on Node.js 11.7.0+ or `gzip, deflate` for older Node.js versions, unless you set it yourself. | ||
Brotli (`br`) support requires Node.js 11.7.0 or later. | ||
console.log(response); | ||
``` | ||
*/ | ||
& ((name: 'uploadProgress' | 'downloadProgress', listener: (progress: Progress) => void) => T) | ||
/** | ||
To enable retrying on a Got stream, it is required to have a `retry` handler attached. | ||
If this is disabled, a compressed response is returned as a `Buffer`. | ||
This may be useful if you want to handle decompression yourself or stream the raw compressed data. | ||
When this event is emitted, you should reset the stream you were writing to and prepare the body again. | ||
@default true | ||
*/ | ||
decompress?: boolean; | ||
/** | ||
Milliseconds to wait for the server to end the response before aborting the request with `got.TimeoutError` error (a.k.a. `request` property). | ||
By default, there's no timeout. | ||
This also accepts an `object` with the following fields to constrain the duration of each phase of the request lifecycle: | ||
- `lookup` starts when a socket is assigned and ends when the hostname has been resolved. | ||
Does not apply when using a Unix domain socket. | ||
- `connect` starts when `lookup` completes (or when the socket is assigned if lookup does not apply to the request) and ends when the socket is connected. | ||
- `secureConnect` starts when `connect` completes and ends when the handshaking process completes (HTTPS only). | ||
- `socket` starts when the socket is connected. See [request.setTimeout](https://nodejs.org/api/http.html#http_request_settimeout_timeout_callback). | ||
- `response` starts when the request has been written to the socket and ends when the response headers are received. | ||
- `send` starts when the socket is connected and ends with the request has been written to the socket. | ||
- `request` starts when the request is initiated and ends when the response's end event fires. | ||
*/ | ||
timeout?: Delays | number; | ||
/** | ||
When specified, `prefixUrl` will be prepended to `url`. | ||
The prefix can be any valid URL, either relative or absolute. | ||
A trailing slash `/` is optional - one will be added automatically. | ||
__Note__: `prefixUrl` will be ignored if the `url` argument is a URL instance. | ||
__Note__: Leading slashes in `input` are disallowed when using this option to enforce consistency and avoid confusion. | ||
For example, when the prefix URL is `https://example.com/foo` and the input is `/bar`, there's ambiguity whether the resulting URL would become `https://example.com/foo/bar` or `https://example.com/bar`. | ||
The latter is used by browsers. | ||
__Tip__: Useful when used with `got.extend()` to create niche-specific Got instances. | ||
__Tip__: You can change `prefixUrl` using hooks as long as the URL still includes the `prefixUrl`. | ||
If the URL doesn't include it anymore, it will throw. | ||
@example | ||
``` | ||
const got = require('got'); | ||
(async () => { | ||
await got('unicorn', {prefixUrl: 'https://cats.com'}); | ||
//=> 'https://cats.com/unicorn' | ||
const instance = got.extend({ | ||
prefixUrl: 'https://google.com' | ||
}); | ||
await instance('unicorn', { | ||
hooks: { | ||
beforeRequest: [ | ||
options => { | ||
options.prefixUrl = 'https://cats.com'; | ||
} | ||
] | ||
} | ||
}); | ||
//=> 'https://cats.com/unicorn' | ||
})(); | ||
``` | ||
*/ | ||
prefixUrl?: string | URL; | ||
/** | ||
__Note #1__: The `body` option cannot be used with the `json` or `form` option. | ||
__Note #2__: If you provide this option, `got.stream()` will be read-only. | ||
__Note #3__: If you provide a payload with the `GET` or `HEAD` method, it will throw a `TypeError` unless the method is `GET` and the `allowGetBody` option is set to `true`. | ||
__Note #4__: This option is not enumerable and will not be merged with the instance defaults. | ||
The `content-length` header will be automatically set if `body` is a `string` / `Buffer` / `fs.createReadStream` instance / [`form-data` instance](https://github.com/form-data/form-data), and `content-length` and `transfer-encoding` are not manually set in `options.headers`. | ||
*/ | ||
body?: string | Buffer | Readable; | ||
/** | ||
The form body is converted to a query string using [`(new URLSearchParams(object)).toString()`](https://nodejs.org/api/url.html#url_constructor_new_urlsearchparams_obj). | ||
If the `Content-Type` header is not present, it will be set to `application/x-www-form-urlencoded`. | ||
__Note #1__: If you provide this option, `got.stream()` will be read-only. | ||
__Note #2__: This option is not enumerable and will not be merged with the instance defaults. | ||
*/ | ||
form?: Record<string, any>; | ||
/** | ||
JSON body. If the `Content-Type` header is not set, it will be set to `application/json`. | ||
__Note #1__: If you provide this option, `got.stream()` will be read-only. | ||
__Note #2__: This option is not enumerable and will not be merged with the instance defaults. | ||
*/ | ||
json?: Record<string, any>; | ||
/** | ||
The URL to request, as a string, a [`https.request` options object](https://nodejs.org/api/https.html#https_https_request_options_callback), or a [WHATWG `URL`](https://nodejs.org/api/url.html#url_class_url). | ||
Properties from `options` will override properties in the parsed `url`. | ||
If no protocol is specified, it will throw a `TypeError`. | ||
__Note__: The query string is **not** parsed as search params. | ||
@example | ||
``` | ||
got('https://example.com/?query=a b'); //=> https://example.com/?query=a%20b | ||
got('https://example.com/', {searchParams: {query: 'a b'}}); //=> https://example.com/?query=a+b | ||
// The query string is overridden by `searchParams` | ||
got('https://example.com/?query=a b', {searchParams: {query: 'a b'}}); //=> https://example.com/?query=a+b | ||
``` | ||
*/ | ||
url?: string | URL; | ||
/** | ||
Cookie support. You don't have to care about parsing or how to store them. | ||
__Note__: If you provide this option, `options.headers.cookie` will be overridden. | ||
*/ | ||
cookieJar?: PromiseCookieJar | ToughCookieJar; | ||
/** | ||
Ignore invalid cookies instead of throwing an error. | ||
Only useful when the `cookieJar` option has been set. Not recommended. | ||
@default false | ||
*/ | ||
ignoreInvalidCookies?: boolean; | ||
/** | ||
Query string that will be added to the request URL. | ||
This will override the query string in `url`. | ||
If you need to pass in an array, you can do it using a `URLSearchParams` instance. | ||
@example | ||
``` | ||
const got = require('got'); | ||
const searchParams = new URLSearchParams([['key', 'a'], ['key', 'b']]); | ||
got('https://example.com', {searchParams}); | ||
console.log(searchParams.toString()); | ||
//=> 'key=a&key=b' | ||
``` | ||
*/ | ||
searchParams?: string | Record<string, string | number | boolean | null | undefined> | URLSearchParams; | ||
/** | ||
An instance of [`CacheableLookup`](https://github.com/szmarczak/cacheable-lookup) used for making DNS lookups. | ||
Useful when making lots of requests to different *public* hostnames. | ||
`CacheableLookup` uses `dns.resolver4(..)` and `dns.resolver6(...)` under the hood and fall backs to `dns.lookup(...)` when the first two fail, which may lead to additional delay. | ||
__Note__: This should stay disabled when making requests to internal hostnames such as `localhost`, `database.local` etc. | ||
@default false | ||
*/ | ||
dnsCache?: CacheableLookup | boolean; | ||
/** | ||
User data. In contrast to other options, `context` is not enumerable. | ||
__Note__: The object is never merged, it's just passed through. | ||
Got will not modify the object in any way. | ||
@example | ||
``` | ||
const got = require('got'); | ||
const instance = got.extend({ | ||
hooks: { | ||
beforeRequest: [ | ||
options => { | ||
if (!options.context || !options.context.token) { | ||
throw new Error('Token required'); | ||
} | ||
options.headers.token = options.context.token; | ||
} | ||
] | ||
} | ||
}); | ||
(async () => { | ||
const context = { | ||
token: 'secret' | ||
}; | ||
const response = await instance('https://httpbin.org/headers', {context}); | ||
// Let's see the headers | ||
console.log(response.body); | ||
})(); | ||
``` | ||
*/ | ||
context?: Record<string, unknown>; | ||
/** | ||
Hooks allow modifications during the request lifecycle. | ||
Hook functions may be async and are run serially. | ||
*/ | ||
hooks?: Hooks; | ||
/** | ||
Defines if redirect responses should be followed automatically. | ||
Note that if a `303` is sent by the server in response to any request type (`POST`, `DELETE`, etc.), Got will automatically request the resource pointed to in the location header via `GET`. | ||
This is in accordance with [the spec](https://tools.ietf.org/html/rfc7231#section-6.4.4). | ||
@default true | ||
*/ | ||
followRedirect?: boolean; | ||
/** | ||
If exceeded, the request will be aborted and a `MaxRedirectsError` will be thrown. | ||
@default 10 | ||
*/ | ||
maxRedirects?: number; | ||
/** | ||
A cache adapter instance for storing cached response data. | ||
@default false | ||
*/ | ||
cache?: string | CacheableRequest.StorageAdapter | false; | ||
/** | ||
Determines if a `got.HTTPError` is thrown for unsuccessful responses. | ||
If this is disabled, requests that encounter an error status code will be resolved with the `response` instead of throwing. | ||
This may be useful if you are checking for resource availability and are expecting error responses. | ||
@default true | ||
*/ | ||
throwHttpErrors?: boolean; | ||
username?: string; | ||
password?: string; | ||
/** | ||
If set to `true`, Got will additionally accept HTTP2 requests. | ||
It will choose either HTTP/1.1 or HTTP/2 depending on the ALPN protocol. | ||
__Note__: Overriding `options.request` will disable HTTP2 support. | ||
__Note__: This option will default to `true` in the next upcoming major release. | ||
@default false | ||
@example | ||
``` | ||
const got = require('got'); | ||
(async () => { | ||
const {headers} = await got('https://nghttp2.org/httpbin/anything', {http2: true}); | ||
console.log(headers.via); | ||
//=> '2 nghttpx' | ||
})(); | ||
``` | ||
*/ | ||
http2?: boolean; | ||
/** | ||
Set this to `true` to allow sending body for the `GET` method. | ||
However, the [HTTP/2 specification](https://tools.ietf.org/html/rfc7540#section-8.1.3) says that `An HTTP GET request includes request header fields and no payload body`, therefore when using the HTTP/2 protocol this option will have no effect. | ||
This option is only meant to interact with non-compliant servers when you have no other choice. | ||
__Note__: The [RFC 7321](https://tools.ietf.org/html/rfc7231#section-4.3.1) doesn't specify any particular behavior for the GET method having a payload, therefore __it's considered an [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern)__. | ||
@default false | ||
*/ | ||
allowGetBody?: boolean; | ||
lookup?: CacheableLookup['lookup']; | ||
/** | ||
Request headers. | ||
Existing headers will be overwritten. Headers set to `undefined` will be omitted. | ||
@default {} | ||
*/ | ||
headers?: Headers; | ||
/** | ||
By default, redirects will use [method rewriting](https://tools.ietf.org/html/rfc7231#section-6.4). | ||
For example, when sending a POST request and receiving a `302`, it will resend the body to the new location using the same HTTP method (`POST` in this case). | ||
@default true | ||
*/ | ||
methodRewriting?: boolean; | ||
/** | ||
Indicates which DNS record family to use. | ||
Values: | ||
- `auto`: IPv4 (if present) or IPv6 | ||
- `ipv4`: Only IPv4 | ||
- `ipv6`: Only IPv6 | ||
__Note__: If you are using the undocumented option `family`, `dnsLookupIpVersion` will override it. | ||
@default 'auto' | ||
*/ | ||
dnsLookupIpVersion?: DnsLookupIpVersion; | ||
/** | ||
A function used to parse JSON responses. | ||
@example | ||
``` | ||
const got = require('got'); | ||
const Bourne = require('@hapi/bourne'); | ||
(async () => { | ||
const parsed = await got('https://example.com', { | ||
parseJson: text => Bourne.parse(text) | ||
}).json(); | ||
console.log(parsed); | ||
})(); | ||
``` | ||
*/ | ||
parseJson?: ParseJsonFunction; | ||
/** | ||
A function used to stringify the body of JSON requests. | ||
@example | ||
``` | ||
const got = require('got'); | ||
(async () => { | ||
await got.post('https://example.com', { | ||
stringifyJson: object => JSON.stringify(object, (key, value) => { | ||
if (key.startsWith('_')) { | ||
return; | ||
} | ||
return value; | ||
}), | ||
json: { | ||
some: 'payload', | ||
_ignoreMe: 1234 | ||
} | ||
}); | ||
})(); | ||
``` | ||
@example | ||
``` | ||
const got = require('got'); | ||
(async () => { | ||
await got.post('https://example.com', { | ||
stringifyJson: object => JSON.stringify(object, (key, value) => { | ||
if (typeof value === 'number') { | ||
return value.toString(); | ||
} | ||
return value; | ||
}), | ||
json: { | ||
some: 'payload', | ||
number: 1 | ||
} | ||
}); | ||
})(); | ||
``` | ||
*/ | ||
stringifyJson?: StringifyJsonFunction; | ||
/** | ||
An object representing `limit`, `calculateDelay`, `methods`, `statusCodes`, `maxRetryAfter` and `errorCodes` fields for maximum retry count, retry handler, allowed methods, allowed status codes, maximum [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time and allowed error codes. | ||
Delays between retries counts with function `1000 * Math.pow(2, retry) + Math.random() * 100`, where `retry` is attempt number (starts from 1). | ||
The `calculateDelay` property is a `function` that receives an object with `attemptCount`, `retryOptions`, `error` and `computedValue` properties for current retry count, the retry options, error and default computed value. | ||
The function must return a delay in milliseconds (or a Promise resolving with it) (`0` return value cancels retry). | ||
By default, it retries *only* on the specified methods, status codes, and on these network errors: | ||
- `ETIMEDOUT`: One of the [timeout](#timeout) limits were reached. | ||
- `ECONNRESET`: Connection was forcibly closed by a peer. | ||
- `EADDRINUSE`: Could not bind to any free port. | ||
- `ECONNREFUSED`: Connection was refused by the server. | ||
- `EPIPE`: The remote side of the stream being written has been closed. | ||
- `ENOTFOUND`: Couldn't resolve the hostname to an IP address. | ||
- `ENETUNREACH`: No internet connection. | ||
- `EAI_AGAIN`: DNS lookup timed out. | ||
__Note__: If `maxRetryAfter` is set to `undefined`, it will use `options.timeout`. | ||
__Note__: If [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header is greater than `maxRetryAfter`, it will cancel the request. | ||
*/ | ||
retry?: Partial<RequiredRetryOptions> | number; | ||
/** | ||
The IP address used to send the request from. | ||
*/ | ||
localAddress?: string; | ||
socketPath?: string; | ||
/** | ||
The HTTP method used to make the request. | ||
@default 'GET' | ||
*/ | ||
method?: Method; | ||
createConnection?: (options: http.RequestOptions, oncreate: (error: Error, socket: Socket) => void) => Socket; | ||
cacheOptions?: CacheOptions; | ||
/** | ||
If set to `false`, all invalid SSL certificates will be ignored and no error will be thrown. | ||
If set to `true`, it will throw an error whenever an invalid SSL certificate is detected. | ||
We strongly recommend to have this set to `true` for security reasons. | ||
@default true | ||
@example | ||
``` | ||
const got = require('got'); | ||
(async () => { | ||
// Correct: | ||
await got('https://example.com', {rejectUnauthorized: true}); | ||
// You can disable it when developing an HTTPS app: | ||
await got('https://localhost', {rejectUnauthorized: false}); | ||
// Never do this: | ||
await got('https://example.com', {rejectUnauthorized: false}); | ||
})(); | ||
``` | ||
*/ | ||
rejectUnauthorized?: boolean; | ||
/** | ||
Options for the advanced HTTPS API. | ||
*/ | ||
https?: HTTPSOptions; | ||
} | ||
export interface Options extends PromiseOnly.Options, PlainOptions { | ||
} | ||
export interface HTTPSOptions { | ||
rejectUnauthorized?: https.RequestOptions['rejectUnauthorized']; | ||
checkServerIdentity?: CheckServerIdentityFunction; | ||
/** | ||
Override the default Certificate Authorities ([from Mozilla](https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReport)). | ||
@example | ||
``` | ||
// Single Certificate Authority | ||
got('https://example.com', { | ||
https: { | ||
certificateAuthority: fs.readFileSync('./my_ca.pem') | ||
} | ||
}); | ||
``` | ||
*/ | ||
certificateAuthority?: SecureContextOptions['ca']; | ||
/** | ||
Private keys in [PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) format. | ||
[PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) allows the option of private keys being encrypted. | ||
Encrypted keys will be decrypted with `options.https.passphrase`. | ||
Multiple keys with different passphrases can be provided as an array of `{pem: <string | Buffer>, passphrase: <string>}` | ||
*/ | ||
key?: SecureContextOptions['key']; | ||
/** | ||
[Certificate chains](https://en.wikipedia.org/wiki/X.509#Certificate_chains_and_cross-certification) in [PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) format. | ||
One cert chain should be provided per private key (`options.https.key`). | ||
When providing multiple cert chains, they do not have to be in the same order as their private keys in `options.https.key`. | ||
If the intermediate certificates are not provided, the peer will not be able to validate the certificate, and the handshake will fail. | ||
*/ | ||
certificate?: SecureContextOptions['cert']; | ||
/** | ||
The passphrase to decrypt the `options.https.key` (if different keys have different passphrases refer to `options.https.key` documentation). | ||
*/ | ||
passphrase?: SecureContextOptions['passphrase']; | ||
pfx?: SecureContextOptions['pfx']; | ||
} | ||
interface NormalizedPlainOptions extends PlainOptions { | ||
method: Method; | ||
url: URL; | ||
timeout: Delays; | ||
prefixUrl: string; | ||
ignoreInvalidCookies: boolean; | ||
decompress: boolean; | ||
searchParams?: URLSearchParams; | ||
cookieJar?: PromiseCookieJar; | ||
headers: Headers; | ||
context: Record<string, unknown>; | ||
hooks: Required<Hooks>; | ||
followRedirect: boolean; | ||
maxRedirects: number; | ||
cache?: string | CacheableRequest.StorageAdapter; | ||
throwHttpErrors: boolean; | ||
dnsCache?: CacheableLookup; | ||
http2: boolean; | ||
allowGetBody: boolean; | ||
rejectUnauthorized: boolean; | ||
lookup?: CacheableLookup['lookup']; | ||
methodRewriting: boolean; | ||
username: string; | ||
password: string; | ||
parseJson: ParseJsonFunction; | ||
stringifyJson: StringifyJsonFunction; | ||
retry: RequiredRetryOptions; | ||
cacheOptions: CacheOptions; | ||
[kRequest]: HttpRequestFunction; | ||
[kIsNormalizedAlready]?: boolean; | ||
} | ||
export interface NormalizedOptions extends PromiseOnly.NormalizedOptions, NormalizedPlainOptions { | ||
} | ||
interface PlainDefaults { | ||
timeout: Delays; | ||
prefixUrl: string; | ||
method: Method; | ||
ignoreInvalidCookies: boolean; | ||
decompress: boolean; | ||
context: Record<string, unknown>; | ||
cookieJar?: PromiseCookieJar | ToughCookieJar; | ||
dnsCache?: CacheableLookup; | ||
headers: Headers; | ||
hooks: Required<Hooks>; | ||
followRedirect: boolean; | ||
maxRedirects: number; | ||
cache?: string | CacheableRequest.StorageAdapter; | ||
throwHttpErrors: boolean; | ||
http2: boolean; | ||
allowGetBody: boolean; | ||
https?: HTTPSOptions; | ||
methodRewriting: boolean; | ||
parseJson: ParseJsonFunction; | ||
stringifyJson: StringifyJsonFunction; | ||
retry: RequiredRetryOptions; | ||
agent?: Agents | false; | ||
request?: RequestFunction; | ||
searchParams?: URLSearchParams; | ||
lookup?: CacheableLookup['lookup']; | ||
localAddress?: string; | ||
createConnection?: Options['createConnection']; | ||
cacheOptions: CacheOptions; | ||
} | ||
export interface Defaults extends PromiseOnly.Defaults, PlainDefaults { | ||
} | ||
export interface Progress { | ||
percent: number; | ||
transferred: number; | ||
total?: number; | ||
} | ||
export interface PlainResponse extends IncomingMessageWithTimings { | ||
/** | ||
The original request URL. | ||
*/ | ||
requestUrl: string; | ||
/** | ||
The redirect URLs. | ||
*/ | ||
redirectUrls: string[]; | ||
/** | ||
- `options` - The Got options that were set on this request. | ||
__Note__: This is not a [http.ClientRequest](https://nodejs.org/api/http.html#http_class_http_clientrequest). | ||
*/ | ||
request: Request; | ||
/** | ||
The remote IP address. | ||
This is hopefully a temporary limitation, see [lukechilds/cacheable-request#86](https://github.com/lukechilds/cacheable-request/issues/86). | ||
__Note__: Not available when the response is cached. | ||
*/ | ||
ip?: string; | ||
/** | ||
Whether the response was retrieved from the cache. | ||
*/ | ||
isFromCache: boolean; | ||
/** | ||
The status code of the response. | ||
*/ | ||
statusCode: number; | ||
/** | ||
The request URL or the final URL after redirects. | ||
*/ | ||
url: string; | ||
/** | ||
The object contains the following properties: | ||
- `start` - Time when the request started. | ||
- `socket` - Time when a socket was assigned to the request. | ||
- `lookup` - Time when the DNS lookup finished. | ||
- `connect` - Time when the socket successfully connected. | ||
- `secureConnect` - Time when the socket securely connected. | ||
- `upload` - Time when the request finished uploading. | ||
- `response` - Time when the request fired `response` event. | ||
- `end` - Time when the response fired `end` event. | ||
- `error` - Time when the request fired `error` event. | ||
- `abort` - Time when the request fired `abort` event. | ||
- `phases` | ||
- `wait` - `timings.socket - timings.start` | ||
- `dns` - `timings.lookup - timings.socket` | ||
- `tcp` - `timings.connect - timings.lookup` | ||
- `tls` - `timings.secureConnect - timings.connect` | ||
- `request` - `timings.upload - (timings.secureConnect || timings.connect)` | ||
- `firstByte` - `timings.response - timings.upload` | ||
- `download` - `timings.end - timings.response` | ||
- `total` - `(timings.end || timings.error || timings.abort) - timings.start` | ||
If something has not been measured yet, it will be `undefined`. | ||
__Note__: The time is a `number` representing the milliseconds elapsed since the UNIX epoch. | ||
*/ | ||
timings: Timings; | ||
/** | ||
The number of times the request was retried. | ||
*/ | ||
retryCount: number; | ||
/** | ||
The raw result of the request. | ||
*/ | ||
rawBody?: Buffer; | ||
/** | ||
The result of the request. | ||
*/ | ||
body?: unknown; | ||
} | ||
export interface Response<T = unknown> extends PlainResponse { | ||
/** | ||
The result of the request. | ||
*/ | ||
body: T; | ||
/** | ||
The raw result of the request. | ||
*/ | ||
rawBody: Buffer; | ||
} | ||
export interface RequestEvents<T> { | ||
/** | ||
`request` event to get the request object of the request. | ||
__Tip__: You can use `request` event to abort requests. | ||
@example | ||
``` | ||
got.stream('https://github.com') | ||
.on('request', request => setTimeout(() => request.destroy(), 50)); | ||
``` | ||
*/ | ||
on: ((name: 'request', listener: (request: http.ClientRequest) => void) => T) | ||
/** | ||
The `response` event to get the response object of the final request. | ||
*/ | ||
& (<R extends Response>(name: 'response', listener: (response: R) => void) => T) | ||
/** | ||
The `redirect` event to get the response object of a redirect. The second argument is options for the next request to the redirect location. | ||
*/ | ||
& (<R extends Response, N extends NormalizedOptions>(name: 'redirect', listener: (response: R, nextOptions: N) => void) => T) | ||
/** | ||
Progress events for uploading (sending a request) and downloading (receiving a response). | ||
The `progress` argument is an object like: | ||
```js | ||
{ | ||
percent: 0.1, | ||
transferred: 1024, | ||
total: 10240 | ||
} | ||
``` | ||
If the `content-length` header is missing, `total` will be `undefined`. | ||
@example | ||
```js | ||
(async () => { | ||
const response = await got('https://sindresorhus.com') | ||
.on('downloadProgress', progress => { | ||
// Report download progress | ||
}) | ||
.on('uploadProgress', progress => { | ||
// Report upload progress | ||
}); | ||
console.log(response); | ||
})(); | ||
``` | ||
*/ | ||
& ((name: 'uploadProgress' | 'downloadProgress', listener: (progress: Progress) => void) => T) | ||
/** | ||
To enable retrying on a Got stream, it is required to have a `retry` handler attached. | ||
When this event is emitted, you should reset the stream you were writing to and prepare the body again. | ||
See `got.options.retry` for more information. | ||
*/ | ||
& ((name: 'retry', listener: (retryCount: number, error: RequestError) => void) => T); | ||
} | ||
export declare const setNonEnumerableProperties: (sources: Array<Options | Defaults | undefined>, to: Options) => void; | ||
/** | ||
An error to be thrown when a request fails. | ||
Contains a `code` property with error class code, like `ECONNREFUSED`. | ||
See `got.options.retry` for more information. | ||
*/ | ||
export declare class RequestError extends Error { | ||
code?: string; | ||
stack: string; | ||
readonly options: NormalizedOptions; | ||
readonly response?: Response; | ||
readonly request?: Request; | ||
readonly timings?: Timings; | ||
constructor(message: string, error: Partial<Error & { | ||
code?: string; | ||
}>, self: Request | NormalizedOptions); | ||
} | ||
/** | ||
An error to be thrown when the server redirects you more than ten times. | ||
Includes a `response` property. | ||
*/ | ||
export declare class MaxRedirectsError extends RequestError { | ||
readonly response: Response; | ||
readonly request: Request; | ||
readonly timings: Timings; | ||
constructor(request: Request); | ||
} | ||
/** | ||
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. | ||
Includes a `response` property. | ||
*/ | ||
export declare class HTTPError extends RequestError { | ||
readonly response: Response; | ||
readonly request: Request; | ||
readonly timings: Timings; | ||
constructor(response: Response); | ||
} | ||
/** | ||
An error to be thrown when a cache method fails. | ||
For example, if the database goes down or there's a filesystem error. | ||
*/ | ||
export declare class CacheError extends RequestError { | ||
readonly request: Request; | ||
constructor(error: Error, request: Request); | ||
} | ||
/** | ||
An error to be thrown when the request body is a stream and an error occurs while reading from that stream. | ||
*/ | ||
export declare class UploadError extends RequestError { | ||
readonly request: Request; | ||
constructor(error: Error, request: Request); | ||
} | ||
/** | ||
An error to be thrown when the request is aborted due to a timeout. | ||
Includes an `event` and `timings` property. | ||
*/ | ||
export declare class TimeoutError extends RequestError { | ||
readonly request: Request; | ||
readonly timings: Timings; | ||
readonly event: string; | ||
constructor(error: TimedOutTimeoutError, timings: Timings, request: Request); | ||
} | ||
/** | ||
An error to be thrown when reading from response stream fails. | ||
*/ | ||
export declare class ReadError extends RequestError { | ||
readonly request: Request; | ||
readonly response: Response; | ||
readonly timings: Timings; | ||
constructor(error: Error, request: Request); | ||
} | ||
/** | ||
An error to be thrown when given an unsupported protocol. | ||
*/ | ||
export declare class UnsupportedProtocolError extends RequestError { | ||
constructor(options: NormalizedOptions); | ||
} | ||
& ((name: 'retry', listener: (retryCount: number, error: RequestError) => void) => T); | ||
export type RequestEvents<T> = { | ||
on: GotEventFunction<T>; | ||
once: GotEventFunction<T>; | ||
off: GotEventFunction<T>; | ||
}; | ||
type UrlType = ConstructorParameters<typeof Options>[0]; | ||
type OptionsType = ConstructorParameters<typeof Options>[1]; | ||
type DefaultsType = ConstructorParameters<typeof Options>[2]; | ||
export default class Request extends Duplex implements RequestEvents<Request> { | ||
['constructor']: typeof Request; | ||
[kUnproxyEvents]: () => void; | ||
_cannotHaveBody: boolean; | ||
[kDownloadedSize]: number; | ||
[kUploadedSize]: number; | ||
[kStopReading]: boolean; | ||
[kTriggerRead]: boolean; | ||
[kBody]: Options['body']; | ||
[kJobs]: Array<() => void>; | ||
[kRetryTimeout]?: NodeJS.Timeout; | ||
[kBodySize]?: number; | ||
[kServerResponsesPiped]: Set<ServerResponse>; | ||
[kIsFromCache]?: boolean; | ||
[kStartedReading]?: boolean; | ||
[kCancelTimeouts]?: () => void; | ||
[kResponseSize]?: number; | ||
[kResponse]?: IncomingMessageWithTimings; | ||
[kOriginalResponse]?: IncomingMessageWithTimings; | ||
[kRequest]?: ClientRequest; | ||
_noPipe?: boolean; | ||
_progressCallbacks: Array<() => void>; | ||
options: NormalizedOptions; | ||
requestUrl: string; | ||
requestInitialized: boolean; | ||
redirects: string[]; | ||
options: Options; | ||
response?: PlainResponse; | ||
requestUrl?: URL; | ||
redirectUrls: URL[]; | ||
retryCount: number; | ||
constructor(url: string | URL | undefined, options?: Options, defaults?: Defaults); | ||
static normalizeArguments(url?: string | URL, options?: Options, defaults?: Defaults): NormalizedOptions; | ||
_lockWrite(): void; | ||
_unlockWrite(): void; | ||
_finalizeBody(): Promise<void>; | ||
_onResponseBase(response: IncomingMessageWithTimings): Promise<void>; | ||
_onResponse(response: IncomingMessageWithTimings): Promise<void>; | ||
_onRequest(request: ClientRequest): void; | ||
_createCacheableRequest(url: URL, options: RequestOptions): Promise<ClientRequest | ResponseLike>; | ||
_makeRequest(): Promise<void>; | ||
_error(error: RequestError): Promise<void>; | ||
private _requestOptions; | ||
private _stopRetry; | ||
private _downloadedSize; | ||
private _uploadedSize; | ||
private _stopReading; | ||
private readonly _pipedServerResponses; | ||
private _request?; | ||
private _responseSize?; | ||
private _bodySize?; | ||
private _unproxyEvents; | ||
private _isFromCache?; | ||
private _cannotHaveBody; | ||
private _triggerRead; | ||
private readonly _jobs; | ||
private _cancelTimeouts; | ||
private readonly _removeListeners; | ||
private _nativeResponse?; | ||
private _flushed; | ||
private _aborted; | ||
private _requestInitialized; | ||
constructor(url: UrlType, options?: OptionsType, defaults?: DefaultsType); | ||
flush(): Promise<void>; | ||
_beforeError(error: Error): void; | ||
_read(): void; | ||
_write(chunk: any, encoding: string | undefined, callback: (error?: Error | null) => void): void; | ||
_writeRequest(chunk: any, encoding: BufferEncoding | undefined, callback: (error?: Error | null) => void): void; | ||
_write(chunk: unknown, encoding: BufferEncoding | undefined, callback: (error?: Error | null) => void): void; | ||
_final(callback: (error?: Error | null) => void): void; | ||
_destroy(error: Error | null, callback: (error: Error | null) => void): void; | ||
get _isAboutToError(): boolean; | ||
pipe<T extends NodeJS.WritableStream>(destination: T, options?: { | ||
end?: boolean; | ||
}): T; | ||
unpipe<T extends NodeJS.WritableStream>(destination: T): this; | ||
private _finalizeBody; | ||
private _onResponseBase; | ||
private _setRawBody; | ||
private _onResponse; | ||
private _onRequest; | ||
private _asyncWrite; | ||
private _sendBody; | ||
private _prepareCache; | ||
private _createCacheableRequest; | ||
private _makeRequest; | ||
private _error; | ||
private _writeRequest; | ||
/** | ||
@@ -1077,3 +146,3 @@ The remote IP address. | ||
*/ | ||
get aborted(): boolean; | ||
get isAborted(): boolean; | ||
get socket(): Socket | undefined; | ||
@@ -1120,7 +189,4 @@ /** | ||
get isFromCache(): boolean | undefined; | ||
pipe<T extends NodeJS.WritableStream>(destination: T, options?: { | ||
end?: boolean; | ||
}): T; | ||
unpipe<T extends NodeJS.WritableStream>(destination: T): this; | ||
get reusedSocket(): boolean | undefined; | ||
} | ||
export {}; |
@@ -1,244 +0,28 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.UnsupportedProtocolError = exports.ReadError = exports.TimeoutError = exports.UploadError = exports.CacheError = exports.HTTPError = exports.MaxRedirectsError = exports.RequestError = exports.setNonEnumerableProperties = exports.knownHookEvents = exports.withoutBody = exports.kIsNormalizedAlready = void 0; | ||
const util_1 = require("util"); | ||
const stream_1 = require("stream"); | ||
const fs_1 = require("fs"); | ||
const url_1 = require("url"); | ||
const http = require("http"); | ||
const http_1 = require("http"); | ||
const https = require("https"); | ||
const http_timer_1 = require("@szmarczak/http-timer"); | ||
const cacheable_lookup_1 = require("cacheable-lookup"); | ||
const CacheableRequest = require("cacheable-request"); | ||
const decompressResponse = require("decompress-response"); | ||
// @ts-expect-error Missing types | ||
const http2wrapper = require("http2-wrapper"); | ||
const lowercaseKeys = require("lowercase-keys"); | ||
const is_1 = require("@sindresorhus/is"); | ||
const get_body_size_1 = require("./utils/get-body-size"); | ||
const is_form_data_1 = require("./utils/is-form-data"); | ||
const proxy_events_1 = require("./utils/proxy-events"); | ||
const timed_out_1 = require("./utils/timed-out"); | ||
const url_to_options_1 = require("./utils/url-to-options"); | ||
const options_to_url_1 = require("./utils/options-to-url"); | ||
const weakable_map_1 = require("./utils/weakable-map"); | ||
const get_buffer_1 = require("./utils/get-buffer"); | ||
const dns_ip_version_1 = require("./utils/dns-ip-version"); | ||
const is_response_ok_1 = require("./utils/is-response-ok"); | ||
const deprecation_warning_1 = require("../utils/deprecation-warning"); | ||
const normalize_arguments_1 = require("../as-promise/normalize-arguments"); | ||
const calculate_retry_delay_1 = require("./calculate-retry-delay"); | ||
let globalDnsCache; | ||
const kRequest = Symbol('request'); | ||
const kResponse = Symbol('response'); | ||
const kResponseSize = Symbol('responseSize'); | ||
const kDownloadedSize = Symbol('downloadedSize'); | ||
const kBodySize = Symbol('bodySize'); | ||
const kUploadedSize = Symbol('uploadedSize'); | ||
const kServerResponsesPiped = Symbol('serverResponsesPiped'); | ||
const kUnproxyEvents = Symbol('unproxyEvents'); | ||
const kIsFromCache = Symbol('isFromCache'); | ||
const kCancelTimeouts = Symbol('cancelTimeouts'); | ||
const kStartedReading = Symbol('startedReading'); | ||
const kStopReading = Symbol('stopReading'); | ||
const kTriggerRead = Symbol('triggerRead'); | ||
const kBody = Symbol('body'); | ||
const kJobs = Symbol('jobs'); | ||
const kOriginalResponse = Symbol('originalResponse'); | ||
const kRetryTimeout = Symbol('retryTimeout'); | ||
exports.kIsNormalizedAlready = Symbol('isNormalizedAlready'); | ||
const supportsBrotli = is_1.default.string(process.versions.brotli); | ||
exports.withoutBody = new Set(['GET', 'HEAD']); | ||
exports.knownHookEvents = [ | ||
'init', | ||
'beforeRequest', | ||
'beforeRedirect', | ||
'beforeError', | ||
'beforeRetry', | ||
// Promise-Only | ||
'afterResponse' | ||
]; | ||
function validateSearchParameters(searchParameters) { | ||
// eslint-disable-next-line guard-for-in | ||
for (const key in searchParameters) { | ||
const value = searchParameters[key]; | ||
if (!is_1.default.string(value) && !is_1.default.number(value) && !is_1.default.boolean(value) && !is_1.default.null_(value) && !is_1.default.undefined(value)) { | ||
throw new TypeError(`The \`searchParams\` value '${String(value)}' must be a string, number, boolean or null`); | ||
} | ||
} | ||
} | ||
function isClientRequest(clientRequest) { | ||
return is_1.default.object(clientRequest) && !('statusCode' in clientRequest); | ||
} | ||
const cacheableStore = new weakable_map_1.default(); | ||
const waitForOpenFile = async (file) => new Promise((resolve, reject) => { | ||
const onError = (error) => { | ||
reject(error); | ||
}; | ||
// Node.js 12 has incomplete types | ||
if (!file.pending) { | ||
resolve(); | ||
} | ||
file.once('error', onError); | ||
file.once('ready', () => { | ||
file.off('error', onError); | ||
resolve(); | ||
}); | ||
}); | ||
import process from 'node:process'; | ||
import { Buffer } from 'node:buffer'; | ||
import { Duplex } from 'node:stream'; | ||
import http, { ServerResponse } from 'node:http'; | ||
import timer from '@szmarczak/http-timer'; | ||
import CacheableRequest, { CacheError as CacheableCacheError, } from 'cacheable-request'; | ||
import decompressResponse from 'decompress-response'; | ||
import is from '@sindresorhus/is'; | ||
import getStream from 'get-stream'; | ||
import { FormDataEncoder, isFormData as isFormDataLike } from 'form-data-encoder'; | ||
import getBodySize from './utils/get-body-size.js'; | ||
import isFormData from './utils/is-form-data.js'; | ||
import proxyEvents from './utils/proxy-events.js'; | ||
import timedOut, { TimeoutError as TimedOutTimeoutError } from './timed-out.js'; | ||
import urlToOptions from './utils/url-to-options.js'; | ||
import WeakableMap from './utils/weakable-map.js'; | ||
import calculateRetryDelay from './calculate-retry-delay.js'; | ||
import Options from './options.js'; | ||
import { isResponseOk } from './response.js'; | ||
import isClientRequest from './utils/is-client-request.js'; | ||
import isUnixSocketURL from './utils/is-unix-socket-url.js'; | ||
import { RequestError, ReadError, MaxRedirectsError, HTTPError, TimeoutError, UploadError, CacheError, AbortError, } from './errors.js'; | ||
const { buffer: getStreamAsBuffer } = getStream; | ||
const supportsBrotli = is.string(process.versions.brotli); | ||
const methodsWithoutBody = new Set(['GET', 'HEAD']); | ||
const cacheableStore = new WeakableMap(); | ||
const redirectCodes = new Set([300, 301, 302, 303, 304, 307, 308]); | ||
const nonEnumerableProperties = [ | ||
'context', | ||
'body', | ||
'json', | ||
'form' | ||
]; | ||
exports.setNonEnumerableProperties = (sources, to) => { | ||
// Non enumerable properties shall not be merged | ||
const properties = {}; | ||
for (const source of sources) { | ||
if (!source) { | ||
continue; | ||
} | ||
for (const name of nonEnumerableProperties) { | ||
if (!(name in source)) { | ||
continue; | ||
} | ||
properties[name] = { | ||
writable: true, | ||
configurable: true, | ||
enumerable: false, | ||
// @ts-expect-error TS doesn't see the check above | ||
value: source[name] | ||
}; | ||
} | ||
} | ||
Object.defineProperties(to, properties); | ||
}; | ||
/** | ||
An error to be thrown when a request fails. | ||
Contains a `code` property with error class code, like `ECONNREFUSED`. | ||
*/ | ||
class RequestError extends Error { | ||
constructor(message, error, self) { | ||
var _a; | ||
super(message); | ||
Error.captureStackTrace(this, this.constructor); | ||
this.name = 'RequestError'; | ||
this.code = error.code; | ||
if (self instanceof Request) { | ||
Object.defineProperty(this, 'request', { | ||
enumerable: false, | ||
value: self | ||
}); | ||
Object.defineProperty(this, 'response', { | ||
enumerable: false, | ||
value: self[kResponse] | ||
}); | ||
Object.defineProperty(this, 'options', { | ||
// This fails because of TS 3.7.2 useDefineForClassFields | ||
// Ref: https://github.com/microsoft/TypeScript/issues/34972 | ||
enumerable: false, | ||
value: self.options | ||
}); | ||
} | ||
else { | ||
Object.defineProperty(this, 'options', { | ||
// This fails because of TS 3.7.2 useDefineForClassFields | ||
// Ref: https://github.com/microsoft/TypeScript/issues/34972 | ||
enumerable: false, | ||
value: self | ||
}); | ||
} | ||
this.timings = (_a = this.request) === null || _a === void 0 ? void 0 : _a.timings; | ||
// Recover the original stacktrace | ||
if (is_1.default.string(error.stack) && is_1.default.string(this.stack)) { | ||
const indexOfMessage = this.stack.indexOf(this.message) + this.message.length; | ||
const thisStackTrace = this.stack.slice(indexOfMessage).split('\n').reverse(); | ||
const errorStackTrace = error.stack.slice(error.stack.indexOf(error.message) + error.message.length).split('\n').reverse(); | ||
// Remove duplicated traces | ||
while (errorStackTrace.length !== 0 && errorStackTrace[0] === thisStackTrace[0]) { | ||
thisStackTrace.shift(); | ||
} | ||
this.stack = `${this.stack.slice(0, indexOfMessage)}${thisStackTrace.reverse().join('\n')}${errorStackTrace.reverse().join('\n')}`; | ||
} | ||
} | ||
} | ||
exports.RequestError = RequestError; | ||
/** | ||
An error to be thrown when the server redirects you more than ten times. | ||
Includes a `response` property. | ||
*/ | ||
class MaxRedirectsError extends RequestError { | ||
constructor(request) { | ||
super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request); | ||
this.name = 'MaxRedirectsError'; | ||
} | ||
} | ||
exports.MaxRedirectsError = MaxRedirectsError; | ||
/** | ||
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. | ||
Includes a `response` property. | ||
*/ | ||
class HTTPError extends RequestError { | ||
constructor(response) { | ||
super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request); | ||
this.name = 'HTTPError'; | ||
} | ||
} | ||
exports.HTTPError = HTTPError; | ||
/** | ||
An error to be thrown when a cache method fails. | ||
For example, if the database goes down or there's a filesystem error. | ||
*/ | ||
class CacheError extends RequestError { | ||
constructor(error, request) { | ||
super(error.message, error, request); | ||
this.name = 'CacheError'; | ||
} | ||
} | ||
exports.CacheError = CacheError; | ||
/** | ||
An error to be thrown when the request body is a stream and an error occurs while reading from that stream. | ||
*/ | ||
class UploadError extends RequestError { | ||
constructor(error, request) { | ||
super(error.message, error, request); | ||
this.name = 'UploadError'; | ||
} | ||
} | ||
exports.UploadError = UploadError; | ||
/** | ||
An error to be thrown when the request is aborted due to a timeout. | ||
Includes an `event` and `timings` property. | ||
*/ | ||
class TimeoutError extends RequestError { | ||
constructor(error, timings, request) { | ||
super(error.message, error, request); | ||
this.name = 'TimeoutError'; | ||
this.event = error.event; | ||
this.timings = timings; | ||
} | ||
} | ||
exports.TimeoutError = TimeoutError; | ||
/** | ||
An error to be thrown when reading from response stream fails. | ||
*/ | ||
class ReadError extends RequestError { | ||
constructor(error, request) { | ||
super(error.message, error, request); | ||
this.name = 'ReadError'; | ||
} | ||
} | ||
exports.ReadError = ReadError; | ||
/** | ||
An error to be thrown when given an unsupported protocol. | ||
*/ | ||
class UnsupportedProtocolError extends RequestError { | ||
constructor(options) { | ||
super(`Unsupported protocol "${options.url.protocol}"`, {}, options); | ||
this.name = 'UnsupportedProtocolError'; | ||
} | ||
} | ||
exports.UnsupportedProtocolError = UnsupportedProtocolError; | ||
const proxiedRequestEvents = [ | ||
@@ -250,528 +34,541 @@ 'socket', | ||
'upgrade', | ||
'timeout' | ||
]; | ||
class Request extends stream_1.Duplex { | ||
constructor(url, options = {}, defaults) { | ||
const noop = () => { }; | ||
export default class Request extends Duplex { | ||
constructor(url, options, defaults) { | ||
super({ | ||
// This must be false, to enable throwing after destroy | ||
// It is used for retry logic in Promise API | ||
// Don't destroy immediately, as the error may be emitted on unsuccessful retry | ||
autoDestroy: false, | ||
// It needs to be zero because we're just proxying the data to another stream | ||
highWaterMark: 0 | ||
highWaterMark: 0, | ||
}); | ||
this[kDownloadedSize] = 0; | ||
this[kUploadedSize] = 0; | ||
this.requestInitialized = false; | ||
this[kServerResponsesPiped] = new Set(); | ||
this.redirects = []; | ||
this[kStopReading] = false; | ||
this[kTriggerRead] = false; | ||
this[kJobs] = []; | ||
// @ts-expect-error - Ignoring for now. | ||
Object.defineProperty(this, 'constructor', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_noPipe", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/9568 | ||
Object.defineProperty(this, "options", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "response", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "requestUrl", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "redirectUrls", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "retryCount", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_stopRetry", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_downloadedSize", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_uploadedSize", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_stopReading", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_pipedServerResponses", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_request", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_responseSize", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_bodySize", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_unproxyEvents", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_isFromCache", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_cannotHaveBody", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_triggerRead", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_cancelTimeouts", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_removeListeners", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_nativeResponse", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_flushed", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_aborted", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
// We need this because `this._request` if `undefined` when using cache | ||
Object.defineProperty(this, "_requestInitialized", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
this._downloadedSize = 0; | ||
this._uploadedSize = 0; | ||
this._stopReading = false; | ||
this._pipedServerResponses = new Set(); | ||
this._cannotHaveBody = false; | ||
this._unproxyEvents = noop; | ||
this._triggerRead = false; | ||
this._cancelTimeouts = noop; | ||
this._removeListeners = noop; | ||
this._jobs = []; | ||
this._flushed = false; | ||
this._requestInitialized = false; | ||
this._aborted = false; | ||
this.redirectUrls = []; | ||
this.retryCount = 0; | ||
// TODO: Remove this when targeting Node.js >= 12 | ||
this._progressCallbacks = []; | ||
const unlockWrite = () => this._unlockWrite(); | ||
const lockWrite = () => this._lockWrite(); | ||
this._stopRetry = noop; | ||
this.on('pipe', (source) => { | ||
source.prependListener('data', unlockWrite); | ||
source.on('data', lockWrite); | ||
source.prependListener('end', unlockWrite); | ||
source.on('end', lockWrite); | ||
if (source?.headers) { | ||
Object.assign(this.options.headers, source.headers); | ||
} | ||
}); | ||
this.on('unpipe', (source) => { | ||
source.off('data', unlockWrite); | ||
source.off('data', lockWrite); | ||
source.off('end', unlockWrite); | ||
source.off('end', lockWrite); | ||
this.on('newListener', event => { | ||
if (event === 'retry' && this.listenerCount('retry') > 0) { | ||
throw new Error('A retry listener has been attached already.'); | ||
} | ||
}); | ||
this.on('pipe', source => { | ||
if (source instanceof http_1.IncomingMessage) { | ||
this.options.headers = { | ||
...source.headers, | ||
...this.options.headers | ||
}; | ||
try { | ||
this.options = new Options(url, options, defaults); | ||
if (!this.options.url) { | ||
if (this.options.prefixUrl === '') { | ||
throw new TypeError('Missing `url` property'); | ||
} | ||
this.options.url = ''; | ||
} | ||
}); | ||
const { json, body, form } = options; | ||
if (json || body || form) { | ||
this._lockWrite(); | ||
this.requestUrl = this.options.url; | ||
} | ||
if (exports.kIsNormalizedAlready in options) { | ||
this.options = options; | ||
} | ||
else { | ||
try { | ||
// @ts-expect-error Common TypeScript bug saying that `this.constructor` is not accessible | ||
this.options = this.constructor.normalizeArguments(url, options, defaults); | ||
catch (error) { | ||
const { options } = error; | ||
if (options) { | ||
this.options = options; | ||
} | ||
catch (error) { | ||
// TODO: Move this to `_destroy()` | ||
if (is_1.default.nodeStream(options.body)) { | ||
options.body.destroy(); | ||
} | ||
this.flush = async () => { | ||
this.flush = async () => { }; | ||
this.destroy(error); | ||
return; | ||
} | ||
}; | ||
return; | ||
} | ||
(async () => { | ||
var _a; | ||
try { | ||
if (this.options.body instanceof fs_1.ReadStream) { | ||
await waitForOpenFile(this.options.body); | ||
// Important! If you replace `body` in a handler with another stream, make sure it's readable first. | ||
// The below is run only once. | ||
const { body } = this.options; | ||
if (is.nodeStream(body)) { | ||
body.once('error', error => { | ||
if (this._flushed) { | ||
this._beforeError(new UploadError(error, this)); | ||
} | ||
const { url: normalizedURL } = this.options; | ||
if (!normalizedURL) { | ||
throw new TypeError('Missing `url` property'); | ||
else { | ||
this.flush = async () => { | ||
this.flush = async () => { }; | ||
this._beforeError(new UploadError(error, this)); | ||
}; | ||
} | ||
this.requestUrl = normalizedURL.toString(); | ||
decodeURI(this.requestUrl); | ||
await this._finalizeBody(); | ||
await this._makeRequest(); | ||
if (this.destroyed) { | ||
(_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.destroy(); | ||
return; | ||
} | ||
// Queued writes etc. | ||
for (const job of this[kJobs]) { | ||
job(); | ||
} | ||
// Prevent memory leak | ||
this[kJobs].length = 0; | ||
this.requestInitialized = true; | ||
}); | ||
} | ||
if (this.options.signal) { | ||
const abort = () => { | ||
this.destroy(new AbortError(this)); | ||
}; | ||
if (this.options.signal.aborted) { | ||
abort(); | ||
} | ||
catch (error) { | ||
if (error instanceof RequestError) { | ||
this._beforeError(error); | ||
return; | ||
} | ||
// This is a workaround for https://github.com/nodejs/node/issues/33335 | ||
if (!this.destroyed) { | ||
this.destroy(error); | ||
} | ||
else { | ||
this.options.signal.addEventListener('abort', abort); | ||
this._removeListeners = () => { | ||
this.options.signal?.removeEventListener('abort', abort); | ||
}; | ||
} | ||
})(); | ||
} | ||
} | ||
static normalizeArguments(url, options, defaults) { | ||
var _a, _b, _c, _d, _e; | ||
const rawOptions = options; | ||
if (is_1.default.object(url) && !is_1.default.urlInstance(url)) { | ||
options = { ...defaults, ...url, ...options }; | ||
async flush() { | ||
if (this._flushed) { | ||
return; | ||
} | ||
else { | ||
if (url && options && options.url !== undefined) { | ||
throw new TypeError('The `url` option is mutually exclusive with the `input` argument'); | ||
this._flushed = true; | ||
try { | ||
await this._finalizeBody(); | ||
if (this.destroyed) { | ||
return; | ||
} | ||
options = { ...defaults, ...options }; | ||
if (url !== undefined) { | ||
options.url = url; | ||
await this._makeRequest(); | ||
if (this.destroyed) { | ||
this._request?.destroy(); | ||
return; | ||
} | ||
if (is_1.default.urlInstance(options.url)) { | ||
options.url = new url_1.URL(options.url.toString()); | ||
// Queued writes etc. | ||
for (const job of this._jobs) { | ||
job(); | ||
} | ||
// Prevent memory leak | ||
this._jobs.length = 0; | ||
this._requestInitialized = true; | ||
} | ||
// TODO: Deprecate URL options in Got 12. | ||
// Support extend-specific options | ||
if (options.cache === false) { | ||
options.cache = undefined; | ||
catch (error) { | ||
this._beforeError(error); | ||
} | ||
if (options.dnsCache === false) { | ||
options.dnsCache = undefined; | ||
} | ||
_beforeError(error) { | ||
if (this._stopReading) { | ||
return; | ||
} | ||
// Nice type assertions | ||
is_1.assert.any([is_1.default.string, is_1.default.undefined], options.method); | ||
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.headers); | ||
is_1.assert.any([is_1.default.string, is_1.default.urlInstance, is_1.default.undefined], options.prefixUrl); | ||
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.cookieJar); | ||
is_1.assert.any([is_1.default.object, is_1.default.string, is_1.default.undefined], options.searchParams); | ||
is_1.assert.any([is_1.default.object, is_1.default.string, is_1.default.undefined], options.cache); | ||
is_1.assert.any([is_1.default.object, is_1.default.number, is_1.default.undefined], options.timeout); | ||
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.context); | ||
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.hooks); | ||
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.decompress); | ||
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.ignoreInvalidCookies); | ||
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.followRedirect); | ||
is_1.assert.any([is_1.default.number, is_1.default.undefined], options.maxRedirects); | ||
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.throwHttpErrors); | ||
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.http2); | ||
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.allowGetBody); | ||
is_1.assert.any([is_1.default.string, is_1.default.undefined], options.localAddress); | ||
is_1.assert.any([dns_ip_version_1.isDnsLookupIpVersion, is_1.default.undefined], options.dnsLookupIpVersion); | ||
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.https); | ||
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.rejectUnauthorized); | ||
if (options.https) { | ||
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.https.rejectUnauthorized); | ||
is_1.assert.any([is_1.default.function_, is_1.default.undefined], options.https.checkServerIdentity); | ||
is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.certificateAuthority); | ||
is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.key); | ||
is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.certificate); | ||
is_1.assert.any([is_1.default.string, is_1.default.undefined], options.https.passphrase); | ||
is_1.assert.any([is_1.default.string, is_1.default.buffer, is_1.default.array, is_1.default.undefined], options.https.pfx); | ||
const { response, options } = this; | ||
const attemptCount = this.retryCount + (error.name === 'RetryError' ? 0 : 1); | ||
this._stopReading = true; | ||
if (!(error instanceof RequestError)) { | ||
error = new RequestError(error.message, error, this); | ||
} | ||
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.cacheOptions); | ||
// `options.method` | ||
if (is_1.default.string(options.method)) { | ||
options.method = options.method.toUpperCase(); | ||
} | ||
else { | ||
options.method = 'GET'; | ||
} | ||
// `options.headers` | ||
if (options.headers === (defaults === null || defaults === void 0 ? void 0 : defaults.headers)) { | ||
options.headers = { ...options.headers }; | ||
} | ||
else { | ||
options.headers = lowercaseKeys({ ...(defaults === null || defaults === void 0 ? void 0 : defaults.headers), ...options.headers }); | ||
} | ||
// Disallow legacy `url.Url` | ||
if ('slashes' in options) { | ||
throw new TypeError('The legacy `url.Url` has been deprecated. Use `URL` instead.'); | ||
} | ||
// `options.auth` | ||
if ('auth' in options) { | ||
throw new TypeError('Parameter `auth` is deprecated. Use `username` / `password` instead.'); | ||
} | ||
// `options.searchParams` | ||
if ('searchParams' in options) { | ||
if (options.searchParams && options.searchParams !== (defaults === null || defaults === void 0 ? void 0 : defaults.searchParams)) { | ||
let searchParameters; | ||
if (is_1.default.string(options.searchParams) || (options.searchParams instanceof url_1.URLSearchParams)) { | ||
searchParameters = new url_1.URLSearchParams(options.searchParams); | ||
const typedError = error; | ||
void (async () => { | ||
// Node.js parser is really weird. | ||
// It emits post-request Parse Errors on the same instance as previous request. WTF. | ||
// Therefore we need to check if it has been destroyed as well. | ||
// | ||
// Furthermore, Node.js 16 `response.destroy()` doesn't immediately destroy the socket, | ||
// but makes the response unreadable. So we additionally need to check `response.readable`. | ||
if (response?.readable && !response.rawBody && !this._request?.socket?.destroyed) { | ||
// @types/node has incorrect typings. `setEncoding` accepts `null` as well. | ||
response.setEncoding(this.readableEncoding); | ||
const success = await this._setRawBody(response); | ||
if (success) { | ||
response.body = response.rawBody.toString(); | ||
} | ||
else { | ||
validateSearchParameters(options.searchParams); | ||
searchParameters = new url_1.URLSearchParams(); | ||
// eslint-disable-next-line guard-for-in | ||
for (const key in options.searchParams) { | ||
const value = options.searchParams[key]; | ||
if (value === null) { | ||
searchParameters.append(key, ''); | ||
} | ||
if (this.listenerCount('retry') !== 0) { | ||
let backoff; | ||
try { | ||
let retryAfter; | ||
if (response && 'retry-after' in response.headers) { | ||
retryAfter = Number(response.headers['retry-after']); | ||
if (Number.isNaN(retryAfter)) { | ||
retryAfter = Date.parse(response.headers['retry-after']) - Date.now(); | ||
if (retryAfter <= 0) { | ||
retryAfter = 1; | ||
} | ||
} | ||
else if (value !== undefined) { | ||
searchParameters.append(key, value); | ||
else { | ||
retryAfter *= 1000; | ||
} | ||
} | ||
const retryOptions = options.retry; | ||
backoff = await retryOptions.calculateDelay({ | ||
attemptCount, | ||
retryOptions, | ||
error: typedError, | ||
retryAfter, | ||
computedValue: calculateRetryDelay({ | ||
attemptCount, | ||
retryOptions, | ||
error: typedError, | ||
retryAfter, | ||
computedValue: retryOptions.maxRetryAfter ?? options.timeout.request ?? Number.POSITIVE_INFINITY, | ||
}), | ||
}); | ||
} | ||
// `normalizeArguments()` is also used to merge options | ||
(_a = defaults === null || defaults === void 0 ? void 0 : defaults.searchParams) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => { | ||
// Only use default if one isn't already defined | ||
if (!searchParameters.has(key)) { | ||
searchParameters.append(key, value); | ||
catch (error_) { | ||
void this._error(new RequestError(error_.message, error_, this)); | ||
return; | ||
} | ||
if (backoff) { | ||
await new Promise(resolve => { | ||
const timeout = setTimeout(resolve, backoff); | ||
this._stopRetry = () => { | ||
clearTimeout(timeout); | ||
resolve(); | ||
}; | ||
}); | ||
// Something forced us to abort the retry | ||
if (this.destroyed) { | ||
return; | ||
} | ||
}); | ||
options.searchParams = searchParameters; | ||
} | ||
} | ||
// `options.username` & `options.password` | ||
options.username = (_b = options.username) !== null && _b !== void 0 ? _b : ''; | ||
options.password = (_c = options.password) !== null && _c !== void 0 ? _c : ''; | ||
// `options.prefixUrl` & `options.url` | ||
if (is_1.default.undefined(options.prefixUrl)) { | ||
options.prefixUrl = (_d = defaults === null || defaults === void 0 ? void 0 : defaults.prefixUrl) !== null && _d !== void 0 ? _d : ''; | ||
} | ||
else { | ||
options.prefixUrl = options.prefixUrl.toString(); | ||
if (options.prefixUrl !== '' && !options.prefixUrl.endsWith('/')) { | ||
options.prefixUrl += '/'; | ||
} | ||
} | ||
if (is_1.default.string(options.url)) { | ||
if (options.url.startsWith('/')) { | ||
throw new Error('`input` must not start with a slash when using `prefixUrl`'); | ||
} | ||
options.url = options_to_url_1.default(options.prefixUrl + options.url, options); | ||
} | ||
else if ((is_1.default.undefined(options.url) && options.prefixUrl !== '') || options.protocol) { | ||
options.url = options_to_url_1.default(options.prefixUrl, options); | ||
} | ||
if (options.url) { | ||
if ('port' in options) { | ||
delete options.port; | ||
} | ||
// Make it possible to change `options.prefixUrl` | ||
let { prefixUrl } = options; | ||
Object.defineProperty(options, 'prefixUrl', { | ||
set: (value) => { | ||
const url = options.url; | ||
if (!url.href.startsWith(value)) { | ||
throw new Error(`Cannot change \`prefixUrl\` from ${prefixUrl} to ${value}: ${url.href}`); | ||
try { | ||
for (const hook of this.options.hooks.beforeRetry) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await hook(typedError, this.retryCount + 1); | ||
} | ||
} | ||
options.url = new url_1.URL(value + url.href.slice(prefixUrl.length)); | ||
prefixUrl = value; | ||
}, | ||
get: () => prefixUrl | ||
}); | ||
// Support UNIX sockets | ||
let { protocol } = options.url; | ||
if (protocol === 'unix:') { | ||
protocol = 'http:'; | ||
options.url = new url_1.URL(`http://unix${options.url.pathname}${options.url.search}`); | ||
catch (error_) { | ||
void this._error(new RequestError(error_.message, error, this)); | ||
return; | ||
} | ||
// Something forced us to abort the retry | ||
if (this.destroyed) { | ||
return; | ||
} | ||
this.destroy(); | ||
this.emit('retry', this.retryCount + 1, error, (updatedOptions) => { | ||
const request = new Request(options.url, updatedOptions, options); | ||
request.retryCount = this.retryCount + 1; | ||
process.nextTick(() => { | ||
void request.flush(); | ||
}); | ||
return request; | ||
}); | ||
return; | ||
} | ||
} | ||
// Set search params | ||
if (options.searchParams) { | ||
// eslint-disable-next-line @typescript-eslint/no-base-to-string | ||
options.url.search = options.searchParams.toString(); | ||
void this._error(typedError); | ||
})(); | ||
} | ||
_read() { | ||
this._triggerRead = true; | ||
const { response } = this; | ||
if (response && !this._stopReading) { | ||
// We cannot put this in the `if` above | ||
// because `.read()` also triggers the `end` event | ||
if (response.readableLength) { | ||
this._triggerRead = false; | ||
} | ||
// Protocol check | ||
if (protocol !== 'http:' && protocol !== 'https:') { | ||
throw new UnsupportedProtocolError(options); | ||
let data; | ||
while ((data = response.read()) !== null) { | ||
this._downloadedSize += data.length; // eslint-disable-line @typescript-eslint/restrict-plus-operands | ||
const progress = this.downloadProgress; | ||
if (progress.percent < 1) { | ||
this.emit('downloadProgress', progress); | ||
} | ||
this.push(data); | ||
} | ||
// Update `username` | ||
if (options.username === '') { | ||
options.username = options.url.username; | ||
} | ||
else { | ||
options.url.username = options.username; | ||
} | ||
// Update `password` | ||
if (options.password === '') { | ||
options.password = options.url.password; | ||
} | ||
else { | ||
options.url.password = options.password; | ||
} | ||
} | ||
// `options.cookieJar` | ||
const { cookieJar } = options; | ||
if (cookieJar) { | ||
let { setCookie, getCookieString } = cookieJar; | ||
is_1.assert.function_(setCookie); | ||
is_1.assert.function_(getCookieString); | ||
/* istanbul ignore next: Horrible `tough-cookie` v3 check */ | ||
if (setCookie.length === 4 && getCookieString.length === 0) { | ||
setCookie = util_1.promisify(setCookie.bind(options.cookieJar)); | ||
getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar)); | ||
options.cookieJar = { | ||
setCookie, | ||
getCookieString: getCookieString | ||
}; | ||
} | ||
} | ||
_write(chunk, encoding, callback) { | ||
const write = () => { | ||
this._writeRequest(chunk, encoding, callback); | ||
}; | ||
if (this._requestInitialized) { | ||
write(); | ||
} | ||
// `options.cache` | ||
const { cache } = options; | ||
if (cache) { | ||
if (!cacheableStore.has(cache)) { | ||
cacheableStore.set(cache, new CacheableRequest(((requestOptions, handler) => { | ||
const result = requestOptions[kRequest](requestOptions, handler); | ||
// TODO: remove this when `cacheable-request` supports async request functions. | ||
if (is_1.default.promise(result)) { | ||
// @ts-expect-error | ||
// We only need to implement the error handler in order to support HTTP2 caching. | ||
// The result will be a promise anyway. | ||
result.once = (event, handler) => { | ||
if (event === 'error') { | ||
result.catch(handler); | ||
} | ||
else if (event === 'abort') { | ||
// The empty catch is needed here in case when | ||
// it rejects before it's `await`ed in `_makeRequest`. | ||
(async () => { | ||
try { | ||
const request = (await result); | ||
request.once('abort', handler); | ||
} | ||
catch (_a) { } | ||
})(); | ||
} | ||
else { | ||
/* istanbul ignore next: safety check */ | ||
throw new Error(`Unknown HTTP2 promise event: ${event}`); | ||
} | ||
return result; | ||
}; | ||
} | ||
return result; | ||
}), cache)); | ||
} | ||
else { | ||
this._jobs.push(write); | ||
} | ||
// `options.cacheOptions` | ||
options.cacheOptions = { ...options.cacheOptions }; | ||
// `options.dnsCache` | ||
if (options.dnsCache === true) { | ||
if (!globalDnsCache) { | ||
globalDnsCache = new cacheable_lookup_1.default(); | ||
} | ||
_final(callback) { | ||
const endRequest = () => { | ||
// We need to check if `this._request` is present, | ||
// because it isn't when we use cache. | ||
if (!this._request || this._request.destroyed) { | ||
callback(); | ||
return; | ||
} | ||
options.dnsCache = globalDnsCache; | ||
this._request.end((error) => { | ||
// The request has been destroyed before `_final` finished. | ||
// See https://github.com/nodejs/node/issues/39356 | ||
if (this._request._writableState?.errored) { | ||
return; | ||
} | ||
if (!error) { | ||
this._bodySize = this._uploadedSize; | ||
this.emit('uploadProgress', this.uploadProgress); | ||
this._request.emit('upload-complete'); | ||
} | ||
callback(error); | ||
}); | ||
}; | ||
if (this._requestInitialized) { | ||
endRequest(); | ||
} | ||
else if (!is_1.default.undefined(options.dnsCache) && !options.dnsCache.lookup) { | ||
throw new TypeError(`Parameter \`dnsCache\` must be a CacheableLookup instance or a boolean, got ${is_1.default(options.dnsCache)}`); | ||
} | ||
// `options.timeout` | ||
if (is_1.default.number(options.timeout)) { | ||
options.timeout = { request: options.timeout }; | ||
} | ||
else if (defaults && options.timeout !== defaults.timeout) { | ||
options.timeout = { | ||
...defaults.timeout, | ||
...options.timeout | ||
}; | ||
} | ||
else { | ||
options.timeout = { ...options.timeout }; | ||
this._jobs.push(endRequest); | ||
} | ||
// `options.context` | ||
if (!options.context) { | ||
options.context = {}; | ||
} | ||
// `options.hooks` | ||
const areHooksDefault = options.hooks === (defaults === null || defaults === void 0 ? void 0 : defaults.hooks); | ||
options.hooks = { ...options.hooks }; | ||
for (const event of exports.knownHookEvents) { | ||
if (event in options.hooks) { | ||
if (is_1.default.array(options.hooks[event])) { | ||
// See https://github.com/microsoft/TypeScript/issues/31445#issuecomment-576929044 | ||
options.hooks[event] = [...options.hooks[event]]; | ||
} | ||
else { | ||
throw new TypeError(`Parameter \`${event}\` must be an Array, got ${is_1.default(options.hooks[event])}`); | ||
} | ||
} | ||
_destroy(error, callback) { | ||
this._stopReading = true; | ||
this.flush = async () => { }; | ||
// Prevent further retries | ||
this._stopRetry(); | ||
this._cancelTimeouts(); | ||
this._removeListeners(); | ||
if (this.options) { | ||
const { body } = this.options; | ||
if (is.nodeStream(body)) { | ||
body.destroy(); | ||
} | ||
else { | ||
options.hooks[event] = []; | ||
} | ||
} | ||
if (defaults && !areHooksDefault) { | ||
for (const event of exports.knownHookEvents) { | ||
const defaultHooks = defaults.hooks[event]; | ||
if (defaultHooks.length > 0) { | ||
// See https://github.com/microsoft/TypeScript/issues/31445#issuecomment-576929044 | ||
options.hooks[event] = [ | ||
...defaults.hooks[event], | ||
...options.hooks[event] | ||
]; | ||
} | ||
} | ||
if (this._request) { | ||
this._request.destroy(); | ||
} | ||
// DNS options | ||
if ('family' in options) { | ||
deprecation_warning_1.default('"options.family" was never documented, please use "options.dnsLookupIpVersion"'); | ||
if (error !== null && !is.undefined(error) && !(error instanceof RequestError)) { | ||
error = new RequestError(error.message, error, this); | ||
} | ||
// HTTPS options | ||
if (defaults === null || defaults === void 0 ? void 0 : defaults.https) { | ||
options.https = { ...defaults.https, ...options.https }; | ||
callback(error); | ||
} | ||
pipe(destination, options) { | ||
if (destination instanceof ServerResponse) { | ||
this._pipedServerResponses.add(destination); | ||
} | ||
if ('rejectUnauthorized' in options) { | ||
deprecation_warning_1.default('"options.rejectUnauthorized" is now deprecated, please use "options.https.rejectUnauthorized"'); | ||
return super.pipe(destination, options); | ||
} | ||
unpipe(destination) { | ||
if (destination instanceof ServerResponse) { | ||
this._pipedServerResponses.delete(destination); | ||
} | ||
if ('checkServerIdentity' in options) { | ||
deprecation_warning_1.default('"options.checkServerIdentity" was never documented, please use "options.https.checkServerIdentity"'); | ||
} | ||
if ('ca' in options) { | ||
deprecation_warning_1.default('"options.ca" was never documented, please use "options.https.certificateAuthority"'); | ||
} | ||
if ('key' in options) { | ||
deprecation_warning_1.default('"options.key" was never documented, please use "options.https.key"'); | ||
} | ||
if ('cert' in options) { | ||
deprecation_warning_1.default('"options.cert" was never documented, please use "options.https.certificate"'); | ||
} | ||
if ('passphrase' in options) { | ||
deprecation_warning_1.default('"options.passphrase" was never documented, please use "options.https.passphrase"'); | ||
} | ||
if ('pfx' in options) { | ||
deprecation_warning_1.default('"options.pfx" was never documented, please use "options.https.pfx"'); | ||
} | ||
// Other options | ||
if ('followRedirects' in options) { | ||
throw new TypeError('The `followRedirects` option does not exist. Use `followRedirect` instead.'); | ||
} | ||
if (options.agent) { | ||
for (const key in options.agent) { | ||
if (key !== 'http' && key !== 'https' && key !== 'http2') { | ||
throw new TypeError(`Expected the \`options.agent\` properties to be \`http\`, \`https\` or \`http2\`, got \`${key}\``); | ||
} | ||
} | ||
} | ||
options.maxRedirects = (_e = options.maxRedirects) !== null && _e !== void 0 ? _e : 0; | ||
// Set non-enumerable properties | ||
exports.setNonEnumerableProperties([defaults, rawOptions], options); | ||
return normalize_arguments_1.default(options, defaults); | ||
super.unpipe(destination); | ||
return this; | ||
} | ||
_lockWrite() { | ||
const onLockedWrite = () => { | ||
throw new TypeError('The payload has been already provided'); | ||
}; | ||
this.write = onLockedWrite; | ||
this.end = onLockedWrite; | ||
} | ||
_unlockWrite() { | ||
this.write = super.write; | ||
this.end = super.end; | ||
} | ||
async _finalizeBody() { | ||
const { options } = this; | ||
const { headers } = options; | ||
const isForm = !is_1.default.undefined(options.form); | ||
const isJSON = !is_1.default.undefined(options.json); | ||
const isBody = !is_1.default.undefined(options.body); | ||
const hasPayload = isForm || isJSON || isBody; | ||
const cannotHaveBody = exports.withoutBody.has(options.method) && !(options.method === 'GET' && options.allowGetBody); | ||
const isForm = !is.undefined(options.form); | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
const isJSON = !is.undefined(options.json); | ||
const isBody = !is.undefined(options.body); | ||
const cannotHaveBody = methodsWithoutBody.has(options.method) && !(options.method === 'GET' && options.allowGetBody); | ||
this._cannotHaveBody = cannotHaveBody; | ||
if (hasPayload) { | ||
if (isForm || isJSON || isBody) { | ||
if (cannotHaveBody) { | ||
throw new TypeError(`The \`${options.method}\` method cannot be used with a body`); | ||
} | ||
if ([isBody, isForm, isJSON].filter(isTrue => isTrue).length > 1) { | ||
throw new TypeError('The `body`, `json` and `form` options are mutually exclusive'); | ||
} | ||
if (isBody && | ||
!(options.body instanceof stream_1.Readable) && | ||
!is_1.default.string(options.body) && | ||
!is_1.default.buffer(options.body) && | ||
!is_form_data_1.default(options.body)) { | ||
throw new TypeError('The `body` option must be a stream.Readable, string or Buffer'); | ||
} | ||
if (isForm && !is_1.default.object(options.form)) { | ||
throw new TypeError('The `form` option must be an Object'); | ||
} | ||
{ | ||
// Serialize body | ||
const noContentType = !is_1.default.string(headers['content-type']); | ||
if (isBody) { | ||
// Special case for https://github.com/form-data/form-data | ||
if (is_form_data_1.default(options.body) && noContentType) { | ||
headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`; | ||
} | ||
this[kBody] = options.body; | ||
} | ||
else if (isForm) { | ||
// Serialize body | ||
const noContentType = !is.string(headers['content-type']); | ||
if (isBody) { | ||
// Body is spec-compliant FormData | ||
if (isFormDataLike(options.body)) { | ||
const encoder = new FormDataEncoder(options.body); | ||
if (noContentType) { | ||
headers['content-type'] = 'application/x-www-form-urlencoded'; | ||
headers['content-type'] = encoder.headers['Content-Type']; | ||
} | ||
this[kBody] = (new url_1.URLSearchParams(options.form)).toString(); | ||
} | ||
else { | ||
if (noContentType) { | ||
headers['content-type'] = 'application/json'; | ||
if ('Content-Length' in encoder.headers) { | ||
headers['content-length'] = encoder.headers['Content-Length']; | ||
} | ||
this[kBody] = options.stringifyJson(options.json); | ||
options.body = encoder.encode(); | ||
} | ||
const uploadBodySize = await get_body_size_1.default(this[kBody], options.headers); | ||
// See https://tools.ietf.org/html/rfc7230#section-3.3.2 | ||
// A user agent SHOULD send a Content-Length in a request message when | ||
// no Transfer-Encoding is sent and the request method defines a meaning | ||
// for an enclosed payload body. For example, a Content-Length header | ||
// field is normally sent in a POST request even when the value is 0 | ||
// (indicating an empty payload body). A user agent SHOULD NOT send a | ||
// Content-Length header field when the request message does not contain | ||
// a payload body and the method semantics do not anticipate such a | ||
// body. | ||
if (is_1.default.undefined(headers['content-length']) && is_1.default.undefined(headers['transfer-encoding'])) { | ||
if (!cannotHaveBody && !is_1.default.undefined(uploadBodySize)) { | ||
headers['content-length'] = String(uploadBodySize); | ||
} | ||
// Special case for https://github.com/form-data/form-data | ||
if (isFormData(options.body) && noContentType) { | ||
headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`; | ||
} | ||
} | ||
else if (isForm) { | ||
if (noContentType) { | ||
headers['content-type'] = 'application/x-www-form-urlencoded'; | ||
} | ||
const { form } = options; | ||
options.form = undefined; | ||
options.body = (new URLSearchParams(form)).toString(); | ||
} | ||
else { | ||
if (noContentType) { | ||
headers['content-type'] = 'application/json'; | ||
} | ||
const { json } = options; | ||
options.json = undefined; | ||
options.body = options.stringifyJson(json); | ||
} | ||
const uploadBodySize = await getBodySize(options.body, options.headers); | ||
// See https://tools.ietf.org/html/rfc7230#section-3.3.2 | ||
// A user agent SHOULD send a Content-Length in a request message when | ||
// no Transfer-Encoding is sent and the request method defines a meaning | ||
// for an enclosed payload body. For example, a Content-Length header | ||
// field is normally sent in a POST request even when the value is 0 | ||
// (indicating an empty payload body). A user agent SHOULD NOT send a | ||
// Content-Length header field when the request message does not contain | ||
// a payload body and the method semantics do not anticipate such a | ||
// body. | ||
if (is.undefined(headers['content-length']) && is.undefined(headers['transfer-encoding']) && !cannotHaveBody && !is.undefined(uploadBodySize)) { | ||
headers['content-length'] = String(uploadBodySize); | ||
} | ||
} | ||
else if (cannotHaveBody) { | ||
this._lockWrite(); | ||
if (options.responseType === 'json' && !('accept' in options.headers)) { | ||
options.headers.accept = 'application/json'; | ||
} | ||
else { | ||
this._unlockWrite(); | ||
} | ||
this[kBodySize] = Number(headers['content-length']) || undefined; | ||
this._bodySize = Number(headers['content-length']) || undefined; | ||
} | ||
async _onResponseBase(response) { | ||
// This will be called e.g. when using cache so we need to check if this request has been aborted. | ||
if (this.isAborted) { | ||
return; | ||
} | ||
const { options } = this; | ||
const { url } = options; | ||
this[kOriginalResponse] = response; | ||
this._nativeResponse = response; | ||
if (options.decompress) { | ||
@@ -782,18 +579,20 @@ response = decompressResponse(response); | ||
const typedResponse = response; | ||
typedResponse.statusMessage = typedResponse.statusMessage ? typedResponse.statusMessage : http.STATUS_CODES[statusCode]; | ||
typedResponse.statusMessage = typedResponse.statusMessage ?? http.STATUS_CODES[statusCode]; | ||
typedResponse.url = options.url.toString(); | ||
typedResponse.requestUrl = this.requestUrl; | ||
typedResponse.redirectUrls = this.redirects; | ||
typedResponse.redirectUrls = this.redirectUrls; | ||
typedResponse.request = this; | ||
typedResponse.isFromCache = response.fromCache || false; | ||
typedResponse.isFromCache = this._nativeResponse.fromCache ?? false; | ||
typedResponse.ip = this.ip; | ||
typedResponse.retryCount = this.retryCount; | ||
this[kIsFromCache] = typedResponse.isFromCache; | ||
this[kResponseSize] = Number(response.headers['content-length']) || undefined; | ||
this[kResponse] = response; | ||
typedResponse.ok = isResponseOk(typedResponse); | ||
this._isFromCache = typedResponse.isFromCache; | ||
this._responseSize = Number(response.headers['content-length']) || undefined; | ||
this.response = typedResponse; | ||
response.once('end', () => { | ||
this[kResponseSize] = this[kDownloadedSize]; | ||
this._responseSize = this._downloadedSize; | ||
this.emit('downloadProgress', this.downloadProgress); | ||
}); | ||
response.once('error', (error) => { | ||
this._aborted = true; | ||
// Force clean-up, because some packages don't do this. | ||
@@ -805,6 +604,7 @@ // TODO: Fix decompress-response | ||
response.once('aborted', () => { | ||
this._aborted = true; | ||
this._beforeError(new ReadError({ | ||
name: 'Error', | ||
message: 'The server aborted pending request', | ||
code: 'ECONNRESET' | ||
code: 'ECONNRESET', | ||
}, this)); | ||
@@ -814,6 +614,11 @@ }); | ||
const rawCookies = response.headers['set-cookie']; | ||
if (is_1.default.object(options.cookieJar) && rawCookies) { | ||
if (is.object(options.cookieJar) && rawCookies) { | ||
let promises = rawCookies.map(async (rawCookie) => options.cookieJar.setCookie(rawCookie, url.toString())); | ||
if (options.ignoreInvalidCookies) { | ||
promises = promises.map(async (p) => p.catch(() => { })); | ||
promises = promises.map(async (promise) => { | ||
try { | ||
await promise; | ||
} | ||
catch { } | ||
}); | ||
} | ||
@@ -828,2 +633,6 @@ try { | ||
} | ||
// The above is running a promise, therefore we need to check if this request has been aborted yet again. | ||
if (this.isAborted) { | ||
return; | ||
} | ||
if (options.followRedirect && response.headers.location && redirectCodes.has(statusCode)) { | ||
@@ -834,63 +643,57 @@ // We're being redirected, we don't care about the response. | ||
response.resume(); | ||
if (this[kRequest]) { | ||
this[kCancelTimeouts](); | ||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete | ||
delete this[kRequest]; | ||
this[kUnproxyEvents](); | ||
} | ||
const shouldBeGet = statusCode === 303 && options.method !== 'GET' && options.method !== 'HEAD'; | ||
if (shouldBeGet || !options.methodRewriting) { | ||
// Server responded with "see other", indicating that the resource exists at another location, | ||
// and the client should request it from that location via GET or HEAD. | ||
options.method = 'GET'; | ||
if ('body' in options) { | ||
delete options.body; | ||
} | ||
if ('json' in options) { | ||
delete options.json; | ||
} | ||
if ('form' in options) { | ||
delete options.form; | ||
} | ||
this[kBody] = undefined; | ||
delete options.headers['content-length']; | ||
} | ||
if (this.redirects.length >= options.maxRedirects) { | ||
this._cancelTimeouts(); | ||
this._unproxyEvents(); | ||
if (this.redirectUrls.length >= options.maxRedirects) { | ||
this._beforeError(new MaxRedirectsError(this)); | ||
return; | ||
} | ||
this._request = undefined; | ||
const updatedOptions = new Options(undefined, undefined, this.options); | ||
const serverRequestedGet = statusCode === 303 && updatedOptions.method !== 'GET' && updatedOptions.method !== 'HEAD'; | ||
const canRewrite = statusCode !== 307 && statusCode !== 308; | ||
const userRequestedGet = updatedOptions.methodRewriting && canRewrite; | ||
if (serverRequestedGet || userRequestedGet) { | ||
updatedOptions.method = 'GET'; | ||
updatedOptions.body = undefined; | ||
updatedOptions.json = undefined; | ||
updatedOptions.form = undefined; | ||
delete updatedOptions.headers['content-length']; | ||
} | ||
try { | ||
// Do not remove. See https://github.com/sindresorhus/got/pull/214 | ||
// We need this in order to support UTF-8 | ||
const redirectBuffer = Buffer.from(response.headers.location, 'binary').toString(); | ||
// Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604 | ||
const redirectUrl = new url_1.URL(redirectBuffer, url); | ||
const redirectString = redirectUrl.toString(); | ||
decodeURI(redirectString); | ||
const redirectUrl = new URL(redirectBuffer, url); | ||
if (!isUnixSocketURL(url) && isUnixSocketURL(redirectUrl)) { | ||
this._beforeError(new RequestError('Cannot redirect to UNIX socket', {}, this)); | ||
return; | ||
} | ||
// Redirecting to a different site, clear sensitive data. | ||
if (redirectUrl.hostname !== url.hostname || redirectUrl.port !== url.port) { | ||
if ('host' in options.headers) { | ||
delete options.headers.host; | ||
if ('host' in updatedOptions.headers) { | ||
delete updatedOptions.headers.host; | ||
} | ||
if ('cookie' in options.headers) { | ||
delete options.headers.cookie; | ||
if ('cookie' in updatedOptions.headers) { | ||
delete updatedOptions.headers.cookie; | ||
} | ||
if ('authorization' in options.headers) { | ||
delete options.headers.authorization; | ||
if ('authorization' in updatedOptions.headers) { | ||
delete updatedOptions.headers.authorization; | ||
} | ||
if (options.username || options.password) { | ||
options.username = ''; | ||
options.password = ''; | ||
if (updatedOptions.username || updatedOptions.password) { | ||
updatedOptions.username = ''; | ||
updatedOptions.password = ''; | ||
} | ||
} | ||
else { | ||
redirectUrl.username = options.username; | ||
redirectUrl.password = options.password; | ||
redirectUrl.username = updatedOptions.username; | ||
redirectUrl.password = updatedOptions.password; | ||
} | ||
this.redirects.push(redirectString); | ||
options.url = redirectUrl; | ||
for (const hook of options.hooks.beforeRedirect) { | ||
this.redirectUrls.push(redirectUrl); | ||
updatedOptions.prefixUrl = ''; | ||
updatedOptions.url = redirectUrl; | ||
for (const hook of updatedOptions.hooks.beforeRedirect) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await hook(options, typedResponse); | ||
await hook(updatedOptions, typedResponse); | ||
} | ||
this.emit('redirect', typedResponse, options); | ||
this.emit('redirect', updatedOptions, typedResponse); | ||
this.options = updatedOptions; | ||
await this._makeRequest(); | ||
@@ -904,3 +707,7 @@ } | ||
} | ||
if (options.isStream && options.throwHttpErrors && !is_response_ok_1.isResponseOk(typedResponse)) { | ||
// `HTTPError`s always have `error.response.body` defined. | ||
// Therefore we cannot retry if `options.throwHttpErrors` is false. | ||
// On the last retry, if `options.throwHttpErrors` is false, we would need to return the body, | ||
// but that wouldn't be possible since the body would be already read in `error.response.body`. | ||
if (options.isStream && options.throwHttpErrors && !isResponseOk(typedResponse)) { | ||
this._beforeError(new HTTPError(typedResponse)); | ||
@@ -910,3 +717,3 @@ return; | ||
response.on('readable', () => { | ||
if (this[kTriggerRead]) { | ||
if (this._triggerRead) { | ||
this._read(); | ||
@@ -924,4 +731,11 @@ } | ||
}); | ||
if (this._noPipe) { | ||
const success = await this._setRawBody(); | ||
if (success) { | ||
this.emit('response', response); | ||
} | ||
return; | ||
} | ||
this.emit('response', response); | ||
for (const destination of this[kServerResponsesPiped]) { | ||
for (const destination of this._pipedServerResponses) { | ||
if (destination.headersSent) { | ||
@@ -941,2 +755,22 @@ continue; | ||
} | ||
async _setRawBody(from = this) { | ||
if (from.readableEnded) { | ||
return false; | ||
} | ||
try { | ||
// Errors are emitted via the `error` event | ||
const rawBody = await getStreamAsBuffer(from); | ||
// TODO: Switch to this: | ||
// let rawBody = await from.toArray(); | ||
// rawBody = Buffer.concat(rawBody); | ||
// On retry Request is destroyed with no error, therefore the above will successfully resolve. | ||
// So in order to check if this was really successfull, we need to check if it has been properly ended. | ||
if (!this.isAborted) { | ||
this.response.rawBody = rawBody; | ||
return true; | ||
} | ||
} | ||
catch { } | ||
return false; | ||
} | ||
async _onResponse(response) { | ||
@@ -954,4 +788,8 @@ try { | ||
const { timeout, url } = options; | ||
http_timer_1.default(request); | ||
this[kCancelTimeouts] = timed_out_1.default(request, timeout, url); | ||
timer(request); | ||
if (this.options.http2) { | ||
// Unset stream timeout, as the `timeout` option was used only for connection timeout. | ||
request.setTimeout(0); | ||
} | ||
this._cancelTimeouts = timedOut(request, timeout, url); | ||
const responseEventName = options.cache ? 'cacheableResponse' : 'response'; | ||
@@ -962,50 +800,112 @@ request.once(responseEventName, (response) => { | ||
request.once('error', (error) => { | ||
var _a; | ||
this._aborted = true; | ||
// Force clean-up, because some packages (e.g. nock) don't do this. | ||
request.destroy(); | ||
// Node.js <= 12.18.2 mistakenly emits the response `end` first. | ||
(_a = request.res) === null || _a === void 0 ? void 0 : _a.removeAllListeners('end'); | ||
error = error instanceof timed_out_1.TimeoutError ? new TimeoutError(error, this.timings, this) : new RequestError(error.message, error, this); | ||
error = error instanceof TimedOutTimeoutError ? new TimeoutError(error, this.timings, this) : new RequestError(error.message, error, this); | ||
this._beforeError(error); | ||
}); | ||
this[kUnproxyEvents] = proxy_events_1.default(request, this, proxiedRequestEvents); | ||
this[kRequest] = request; | ||
this._unproxyEvents = proxyEvents(request, this, proxiedRequestEvents); | ||
this._request = request; | ||
this.emit('uploadProgress', this.uploadProgress); | ||
this._sendBody(); | ||
this.emit('request', request); | ||
} | ||
async _asyncWrite(chunk) { | ||
return new Promise((resolve, reject) => { | ||
super.write(chunk, error => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
_sendBody() { | ||
// Send body | ||
const body = this[kBody]; | ||
const currentRequest = this.redirects.length === 0 ? this : request; | ||
if (is_1.default.nodeStream(body)) { | ||
const { body } = this.options; | ||
const currentRequest = this.redirectUrls.length === 0 ? this : this._request ?? this; | ||
if (is.nodeStream(body)) { | ||
body.pipe(currentRequest); | ||
body.once('error', (error) => { | ||
this._beforeError(new UploadError(error, this)); | ||
}); | ||
} | ||
else { | ||
this._unlockWrite(); | ||
if (!is_1.default.undefined(body)) { | ||
this._writeRequest(body, undefined, () => { }); | ||
currentRequest.end(); | ||
this._lockWrite(); | ||
} | ||
else if (this._cannotHaveBody || this._noPipe) { | ||
currentRequest.end(); | ||
this._lockWrite(); | ||
} | ||
else if (is.generator(body) || is.asyncGenerator(body)) { | ||
(async () => { | ||
try { | ||
for await (const chunk of body) { | ||
await this._asyncWrite(chunk); | ||
} | ||
super.end(); | ||
} | ||
catch (error) { | ||
this._beforeError(error); | ||
} | ||
})(); | ||
} | ||
this.emit('request', request); | ||
else if (!is.undefined(body)) { | ||
this._writeRequest(body, undefined, () => { }); | ||
currentRequest.end(); | ||
} | ||
else if (this._cannotHaveBody || this._noPipe) { | ||
currentRequest.end(); | ||
} | ||
} | ||
_prepareCache(cache) { | ||
if (!cacheableStore.has(cache)) { | ||
const cacheableRequest = new CacheableRequest(((requestOptions, handler) => { | ||
const result = requestOptions._request(requestOptions, handler); | ||
// TODO: remove this when `cacheable-request` supports async request functions. | ||
if (is.promise(result)) { | ||
// We only need to implement the error handler in order to support HTTP2 caching. | ||
// The result will be a promise anyway. | ||
// @ts-expect-error ignore | ||
result.once = (event, handler) => { | ||
if (event === 'error') { | ||
(async () => { | ||
try { | ||
await result; | ||
} | ||
catch (error) { | ||
handler(error); | ||
} | ||
})(); | ||
} | ||
else if (event === 'abort') { | ||
// The empty catch is needed here in case when | ||
// it rejects before it's `await`ed in `_makeRequest`. | ||
(async () => { | ||
try { | ||
const request = (await result); | ||
request.once('abort', handler); | ||
} | ||
catch { } | ||
})(); | ||
} | ||
else { | ||
/* istanbul ignore next: safety check */ | ||
throw new Error(`Unknown HTTP2 promise event: ${event}`); | ||
} | ||
return result; | ||
}; | ||
} | ||
return result; | ||
}), cache); | ||
cacheableStore.set(cache, cacheableRequest.request()); | ||
} | ||
} | ||
async _createCacheableRequest(url, options) { | ||
return new Promise((resolve, reject) => { | ||
// TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed | ||
Object.assign(options, url_to_options_1.default(url)); | ||
// `http-cache-semantics` checks this | ||
// TODO: Fix this ignore. | ||
// @ts-expect-error | ||
delete options.url; | ||
Object.assign(options, urlToOptions(url)); | ||
let request; | ||
// This is ugly | ||
// TODO: Fix `cacheable-response`. This is ugly. | ||
const cacheRequest = cacheableStore.get(options.cache)(options, async (response) => { | ||
// TODO: Fix `cacheable-response` | ||
response._readableState.autoDestroy = false; | ||
if (request) { | ||
const fix = () => { | ||
if (response.req) { | ||
response.complete = response.req.res.complete; | ||
} | ||
}; | ||
response.prependOnceListener('end', fix); | ||
fix(); | ||
(await request).emit('cacheableResponse', response); | ||
@@ -1015,4 +915,2 @@ } | ||
}); | ||
// Restore options | ||
options.url = url; | ||
cacheRequest.once('error', reject); | ||
@@ -1026,149 +924,69 @@ cacheRequest.once('request', async (requestOrPromise) => { | ||
async _makeRequest() { | ||
var _a, _b, _c, _d, _e; | ||
const { options } = this; | ||
const { headers } = options; | ||
const { headers, username, password } = options; | ||
const cookieJar = options.cookieJar; | ||
for (const key in headers) { | ||
if (is_1.default.undefined(headers[key])) { | ||
if (is.undefined(headers[key])) { | ||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete | ||
delete headers[key]; | ||
} | ||
else if (is_1.default.null_(headers[key])) { | ||
else if (is.null_(headers[key])) { | ||
throw new TypeError(`Use \`undefined\` instead of \`null\` to delete the \`${key}\` header`); | ||
} | ||
} | ||
if (options.decompress && is_1.default.undefined(headers['accept-encoding'])) { | ||
if (options.decompress && is.undefined(headers['accept-encoding'])) { | ||
headers['accept-encoding'] = supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate'; | ||
} | ||
if (username || password) { | ||
const credentials = Buffer.from(`${username}:${password}`).toString('base64'); | ||
headers.authorization = `Basic ${credentials}`; | ||
} | ||
// Set cookies | ||
if (options.cookieJar) { | ||
const cookieString = await options.cookieJar.getCookieString(options.url.toString()); | ||
if (is_1.default.nonEmptyString(cookieString)) { | ||
options.headers.cookie = cookieString; | ||
if (cookieJar) { | ||
const cookieString = await cookieJar.getCookieString(options.url.toString()); | ||
if (is.nonEmptyString(cookieString)) { | ||
headers.cookie = cookieString; | ||
} | ||
} | ||
// Reset `prefixUrl` | ||
options.prefixUrl = ''; | ||
let request; | ||
for (const hook of options.hooks.beforeRequest) { | ||
// eslint-disable-next-line no-await-in-loop | ||
const result = await hook(options); | ||
if (!is_1.default.undefined(result)) { | ||
if (!is.undefined(result)) { | ||
// @ts-expect-error Skip the type mismatch to support abstract responses | ||
options.request = () => result; | ||
request = () => result; | ||
break; | ||
} | ||
} | ||
if (options.body && this[kBody] !== options.body) { | ||
this[kBody] = options.body; | ||
if (!request) { | ||
request = options.getRequestFunction(); | ||
} | ||
const { agent, request, timeout, url } = options; | ||
if (options.dnsCache && !('lookup' in options)) { | ||
options.lookup = options.dnsCache.lookup; | ||
const url = options.url; | ||
this._requestOptions = options.createNativeRequestOptions(); | ||
if (options.cache) { | ||
this._requestOptions._request = request; | ||
this._requestOptions.cache = options.cache; | ||
this._requestOptions.body = options.body; | ||
this._prepareCache(options.cache); | ||
} | ||
// UNIX sockets | ||
if (url.hostname === 'unix') { | ||
const matches = /(?<socketPath>.+?):(?<path>.+)/.exec(`${url.pathname}${url.search}`); | ||
if (matches === null || matches === void 0 ? void 0 : matches.groups) { | ||
const { socketPath, path } = matches.groups; | ||
Object.assign(options, { | ||
socketPath, | ||
path, | ||
host: '' | ||
}); | ||
} | ||
} | ||
const isHttps = url.protocol === 'https:'; | ||
// Fallback function | ||
let fallbackFn; | ||
if (options.http2) { | ||
fallbackFn = http2wrapper.auto; | ||
} | ||
else { | ||
fallbackFn = isHttps ? https.request : http.request; | ||
} | ||
const realFn = (_a = options.request) !== null && _a !== void 0 ? _a : fallbackFn; | ||
// Cache support | ||
const fn = options.cache ? this._createCacheableRequest : realFn; | ||
// Pass an agent directly when HTTP2 is disabled | ||
if (agent && !options.http2) { | ||
options.agent = agent[isHttps ? 'https' : 'http']; | ||
} | ||
// Prepare plain HTTP request options | ||
options[kRequest] = realFn; | ||
delete options.request; | ||
// TODO: Fix this ignore. | ||
// @ts-expect-error | ||
delete options.timeout; | ||
const requestOptions = options; | ||
requestOptions.shared = (_b = options.cacheOptions) === null || _b === void 0 ? void 0 : _b.shared; | ||
requestOptions.cacheHeuristic = (_c = options.cacheOptions) === null || _c === void 0 ? void 0 : _c.cacheHeuristic; | ||
requestOptions.immutableMinTimeToLive = (_d = options.cacheOptions) === null || _d === void 0 ? void 0 : _d.immutableMinTimeToLive; | ||
requestOptions.ignoreCargoCult = (_e = options.cacheOptions) === null || _e === void 0 ? void 0 : _e.ignoreCargoCult; | ||
// If `dnsLookupIpVersion` is not present do not override `family` | ||
if (options.dnsLookupIpVersion !== undefined) { | ||
try { | ||
requestOptions.family = dns_ip_version_1.dnsLookupIpVersionToFamily(options.dnsLookupIpVersion); | ||
} | ||
catch (_f) { | ||
throw new Error('Invalid `dnsLookupIpVersion` option value'); | ||
} | ||
} | ||
// HTTPS options remapping | ||
if (options.https) { | ||
if ('rejectUnauthorized' in options.https) { | ||
requestOptions.rejectUnauthorized = options.https.rejectUnauthorized; | ||
} | ||
if (options.https.checkServerIdentity) { | ||
requestOptions.checkServerIdentity = options.https.checkServerIdentity; | ||
} | ||
if (options.https.certificateAuthority) { | ||
requestOptions.ca = options.https.certificateAuthority; | ||
} | ||
if (options.https.certificate) { | ||
requestOptions.cert = options.https.certificate; | ||
} | ||
if (options.https.key) { | ||
requestOptions.key = options.https.key; | ||
} | ||
if (options.https.passphrase) { | ||
requestOptions.passphrase = options.https.passphrase; | ||
} | ||
if (options.https.pfx) { | ||
requestOptions.pfx = options.https.pfx; | ||
} | ||
} | ||
const fn = options.cache ? this._createCacheableRequest : request; | ||
try { | ||
let requestOrResponse = await fn(url, requestOptions); | ||
if (is_1.default.undefined(requestOrResponse)) { | ||
requestOrResponse = fallbackFn(url, requestOptions); | ||
// We can't do `await fn(...)`, | ||
// because stream `error` event can be emitted before `Promise.resolve()`. | ||
let requestOrResponse = fn(url, this._requestOptions); | ||
if (is.promise(requestOrResponse)) { | ||
requestOrResponse = await requestOrResponse; | ||
} | ||
// Restore options | ||
options.request = request; | ||
options.timeout = timeout; | ||
options.agent = agent; | ||
// HTTPS options restore | ||
if (options.https) { | ||
if ('rejectUnauthorized' in options.https) { | ||
delete requestOptions.rejectUnauthorized; | ||
// Fallback | ||
if (is.undefined(requestOrResponse)) { | ||
requestOrResponse = options.getFallbackRequestFunction()(url, this._requestOptions); | ||
if (is.promise(requestOrResponse)) { | ||
requestOrResponse = await requestOrResponse; | ||
} | ||
if (options.https.checkServerIdentity) { | ||
// @ts-expect-error - This one will be removed when we remove the alias. | ||
delete requestOptions.checkServerIdentity; | ||
} | ||
if (options.https.certificateAuthority) { | ||
delete requestOptions.ca; | ||
} | ||
if (options.https.certificate) { | ||
delete requestOptions.cert; | ||
} | ||
if (options.https.key) { | ||
delete requestOptions.key; | ||
} | ||
if (options.https.passphrase) { | ||
delete requestOptions.passphrase; | ||
} | ||
if (options.https.pfx) { | ||
delete requestOptions.pfx; | ||
} | ||
} | ||
if (isClientRequest(requestOrResponse)) { | ||
this._onRequest(requestOrResponse); | ||
// Emit the response after the stream has been ended | ||
} | ||
@@ -1179,5 +997,3 @@ else if (this.writable) { | ||
}); | ||
this._unlockWrite(); | ||
this.end(); | ||
this._lockWrite(); | ||
this._sendBody(); | ||
} | ||
@@ -1189,6 +1005,6 @@ else { | ||
catch (error) { | ||
if (error instanceof CacheableRequest.CacheError) { | ||
if (error instanceof CacheableCacheError) { | ||
throw new CacheError(error, this); | ||
} | ||
throw new RequestError(error.message, error, this); | ||
throw error; | ||
} | ||
@@ -1198,6 +1014,13 @@ } | ||
try { | ||
for (const hook of this.options.hooks.beforeError) { | ||
// eslint-disable-next-line no-await-in-loop | ||
error = await hook(error); | ||
if (error instanceof HTTPError && !this.options.throwHttpErrors) { | ||
// This branch can be reached only when using the Promise API | ||
// Skip calling the hooks on purpose. | ||
// See https://github.com/sindresorhus/got/issues/2103 | ||
} | ||
else { | ||
for (const hook of this.options.hooks.beforeError) { | ||
// eslint-disable-next-line no-await-in-loop | ||
error = await hook(error); | ||
} | ||
} | ||
} | ||
@@ -1209,189 +1032,19 @@ catch (error_) { | ||
} | ||
_beforeError(error) { | ||
if (this[kStopReading]) { | ||
_writeRequest(chunk, encoding, callback) { | ||
if (!this._request || this._request.destroyed) { | ||
// Probably the `ClientRequest` instance will throw | ||
return; | ||
} | ||
const { options } = this; | ||
const retryCount = this.retryCount + 1; | ||
this[kStopReading] = true; | ||
if (!(error instanceof RequestError)) { | ||
error = new RequestError(error.message, error, this); | ||
} | ||
const typedError = error; | ||
const { response } = typedError; | ||
void (async () => { | ||
if (response && !response.body) { | ||
response.setEncoding(this._readableState.encoding); | ||
try { | ||
response.rawBody = await get_buffer_1.default(response); | ||
response.body = response.rawBody.toString(); | ||
} | ||
catch (_a) { } | ||
} | ||
if (this.listenerCount('retry') !== 0) { | ||
let backoff; | ||
try { | ||
let retryAfter; | ||
if (response && 'retry-after' in response.headers) { | ||
retryAfter = Number(response.headers['retry-after']); | ||
if (Number.isNaN(retryAfter)) { | ||
retryAfter = Date.parse(response.headers['retry-after']) - Date.now(); | ||
if (retryAfter <= 0) { | ||
retryAfter = 1; | ||
} | ||
} | ||
else { | ||
retryAfter *= 1000; | ||
} | ||
} | ||
backoff = await options.retry.calculateDelay({ | ||
attemptCount: retryCount, | ||
retryOptions: options.retry, | ||
error: typedError, | ||
retryAfter, | ||
computedValue: calculate_retry_delay_1.default({ | ||
attemptCount: retryCount, | ||
retryOptions: options.retry, | ||
error: typedError, | ||
retryAfter, | ||
computedValue: 0 | ||
}) | ||
}); | ||
} | ||
catch (error_) { | ||
void this._error(new RequestError(error_.message, error_, this)); | ||
return; | ||
} | ||
if (backoff) { | ||
const retry = async () => { | ||
try { | ||
for (const hook of this.options.hooks.beforeRetry) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await hook(this.options, typedError, retryCount); | ||
} | ||
} | ||
catch (error_) { | ||
void this._error(new RequestError(error_.message, error, this)); | ||
return; | ||
} | ||
// Something forced us to abort the retry | ||
if (this.destroyed) { | ||
return; | ||
} | ||
this.destroy(); | ||
this.emit('retry', retryCount, error); | ||
}; | ||
this[kRetryTimeout] = setTimeout(retry, backoff); | ||
return; | ||
} | ||
} | ||
void this._error(typedError); | ||
})(); | ||
} | ||
_read() { | ||
this[kTriggerRead] = true; | ||
const response = this[kResponse]; | ||
if (response && !this[kStopReading]) { | ||
// We cannot put this in the `if` above | ||
// because `.read()` also triggers the `end` event | ||
if (response.readableLength) { | ||
this[kTriggerRead] = false; | ||
} | ||
let data; | ||
while ((data = response.read()) !== null) { | ||
this[kDownloadedSize] += data.length; | ||
this[kStartedReading] = true; | ||
const progress = this.downloadProgress; | ||
this._request.write(chunk, encoding, (error) => { | ||
// The `!destroyed` check is required to prevent `uploadProgress` being emitted after the stream was destroyed | ||
if (!error && !this._request.destroyed) { | ||
this._uploadedSize += Buffer.byteLength(chunk, encoding); | ||
const progress = this.uploadProgress; | ||
if (progress.percent < 1) { | ||
this.emit('downloadProgress', progress); | ||
this.emit('uploadProgress', progress); | ||
} | ||
this.push(data); | ||
} | ||
} | ||
} | ||
// Node.js 12 has incorrect types, so the encoding must be a string | ||
_write(chunk, encoding, callback) { | ||
const write = () => { | ||
this._writeRequest(chunk, encoding, callback); | ||
}; | ||
if (this.requestInitialized) { | ||
write(); | ||
} | ||
else { | ||
this[kJobs].push(write); | ||
} | ||
} | ||
_writeRequest(chunk, encoding, callback) { | ||
if (this[kRequest].destroyed) { | ||
// Probably the `ClientRequest` instance will throw | ||
return; | ||
} | ||
this._progressCallbacks.push(() => { | ||
this[kUploadedSize] += Buffer.byteLength(chunk, encoding); | ||
const progress = this.uploadProgress; | ||
if (progress.percent < 1) { | ||
this.emit('uploadProgress', progress); | ||
} | ||
}); | ||
// TODO: What happens if it's from cache? Then this[kRequest] won't be defined. | ||
this[kRequest].write(chunk, encoding, (error) => { | ||
if (!error && this._progressCallbacks.length > 0) { | ||
this._progressCallbacks.shift()(); | ||
} | ||
callback(error); | ||
}); | ||
} | ||
_final(callback) { | ||
const endRequest = () => { | ||
// FIX: Node.js 10 calls the write callback AFTER the end callback! | ||
while (this._progressCallbacks.length !== 0) { | ||
this._progressCallbacks.shift()(); | ||
} | ||
// We need to check if `this[kRequest]` is present, | ||
// because it isn't when we use cache. | ||
if (!(kRequest in this)) { | ||
callback(); | ||
return; | ||
} | ||
if (this[kRequest].destroyed) { | ||
callback(); | ||
return; | ||
} | ||
this[kRequest].end((error) => { | ||
if (!error) { | ||
this[kBodySize] = this[kUploadedSize]; | ||
this.emit('uploadProgress', this.uploadProgress); | ||
this[kRequest].emit('upload-complete'); | ||
} | ||
callback(error); | ||
}); | ||
}; | ||
if (this.requestInitialized) { | ||
endRequest(); | ||
} | ||
else { | ||
this[kJobs].push(endRequest); | ||
} | ||
} | ||
_destroy(error, callback) { | ||
var _a; | ||
this[kStopReading] = true; | ||
// Prevent further retries | ||
clearTimeout(this[kRetryTimeout]); | ||
if (kRequest in this) { | ||
this[kCancelTimeouts](); | ||
// TODO: Remove the next `if` when these get fixed: | ||
// - https://github.com/nodejs/node/issues/32851 | ||
if (!((_a = this[kResponse]) === null || _a === void 0 ? void 0 : _a.complete)) { | ||
this[kRequest].destroy(); | ||
} | ||
} | ||
if (error !== null && !is_1.default.undefined(error) && !(error instanceof RequestError)) { | ||
error = new RequestError(error.message, error, this); | ||
} | ||
callback(error); | ||
} | ||
get _isAboutToError() { | ||
return this[kStopReading]; | ||
} | ||
/** | ||
@@ -1401,4 +1054,3 @@ The remote IP address. | ||
get ip() { | ||
var _a; | ||
return (_a = this.socket) === null || _a === void 0 ? void 0 : _a.remoteAddress; | ||
return this.socket?.remoteAddress; | ||
} | ||
@@ -1408,9 +1060,7 @@ /** | ||
*/ | ||
get aborted() { | ||
var _a, _b, _c; | ||
return ((_b = (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.destroyed) !== null && _b !== void 0 ? _b : this.destroyed) && !((_c = this[kOriginalResponse]) === null || _c === void 0 ? void 0 : _c.complete); | ||
get isAborted() { | ||
return this._aborted; | ||
} | ||
get socket() { | ||
var _a, _b; | ||
return (_b = (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket) !== null && _b !== void 0 ? _b : undefined; | ||
return this._request?.socket ?? undefined; | ||
} | ||
@@ -1422,6 +1072,6 @@ /** | ||
let percent; | ||
if (this[kResponseSize]) { | ||
percent = this[kDownloadedSize] / this[kResponseSize]; | ||
if (this._responseSize) { | ||
percent = this._downloadedSize / this._responseSize; | ||
} | ||
else if (this[kResponseSize] === this[kDownloadedSize]) { | ||
else if (this._responseSize === this._downloadedSize) { | ||
percent = 1; | ||
@@ -1434,4 +1084,4 @@ } | ||
percent, | ||
transferred: this[kDownloadedSize], | ||
total: this[kResponseSize] | ||
transferred: this._downloadedSize, | ||
total: this._responseSize, | ||
}; | ||
@@ -1444,6 +1094,6 @@ } | ||
let percent; | ||
if (this[kBodySize]) { | ||
percent = this[kUploadedSize] / this[kBodySize]; | ||
if (this._bodySize) { | ||
percent = this._uploadedSize / this._bodySize; | ||
} | ||
else if (this[kBodySize] === this[kUploadedSize]) { | ||
else if (this._bodySize === this._uploadedSize) { | ||
percent = 1; | ||
@@ -1456,4 +1106,4 @@ } | ||
percent, | ||
transferred: this[kUploadedSize], | ||
total: this[kBodySize] | ||
transferred: this._uploadedSize, | ||
total: this._bodySize, | ||
}; | ||
@@ -1489,4 +1139,3 @@ } | ||
get timings() { | ||
var _a; | ||
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.timings; | ||
return this._request?.timings; | ||
} | ||
@@ -1497,21 +1146,7 @@ /** | ||
get isFromCache() { | ||
return this[kIsFromCache]; | ||
return this._isFromCache; | ||
} | ||
pipe(destination, options) { | ||
if (this[kStartedReading]) { | ||
throw new Error('Failed to pipe. The response has been emitted already.'); | ||
} | ||
if (destination instanceof http_1.ServerResponse) { | ||
this[kServerResponsesPiped].add(destination); | ||
} | ||
return super.pipe(destination, options); | ||
get reusedSocket() { | ||
return this._request?.reusedSocket; | ||
} | ||
unpipe(destination) { | ||
if (destination instanceof http_1.ServerResponse) { | ||
this[kServerResponsesPiped].delete(destination); | ||
} | ||
super.unpipe(destination); | ||
return this; | ||
} | ||
} | ||
exports.default = Request; |
@@ -1,3 +0,3 @@ | ||
import { ClientRequestArgs } from 'http'; | ||
declare const _default: (body: unknown, headers: ClientRequestArgs['headers']) => Promise<number | undefined>; | ||
export default _default; | ||
/// <reference types="node" resolution-mode="require"/> | ||
import type { ClientRequestArgs } from 'node:http'; | ||
export default function getBodySize(body: unknown, headers: ClientRequestArgs['headers']): Promise<number | undefined>; |
@@ -1,9 +0,6 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fs_1 = require("fs"); | ||
const util_1 = require("util"); | ||
const is_1 = require("@sindresorhus/is"); | ||
const is_form_data_1 = require("./is-form-data"); | ||
const statAsync = util_1.promisify(fs_1.stat); | ||
exports.default = async (body, headers) => { | ||
import { Buffer } from 'node:buffer'; | ||
import { promisify } from 'node:util'; | ||
import is from '@sindresorhus/is'; | ||
import isFormData from './is-form-data.js'; | ||
export default async function getBodySize(body, headers) { | ||
if (headers && 'content-length' in headers) { | ||
@@ -15,19 +12,12 @@ return Number(headers['content-length']); | ||
} | ||
if (is_1.default.string(body)) { | ||
if (is.string(body)) { | ||
return Buffer.byteLength(body); | ||
} | ||
if (is_1.default.buffer(body)) { | ||
if (is.buffer(body)) { | ||
return body.length; | ||
} | ||
if (is_form_data_1.default(body)) { | ||
return util_1.promisify(body.getLength.bind(body))(); | ||
if (isFormData(body)) { | ||
return promisify(body.getLength.bind(body))(); | ||
} | ||
if (body instanceof fs_1.ReadStream) { | ||
const { size } = await statAsync(body.path); | ||
if (size === 0) { | ||
return undefined; | ||
} | ||
return size; | ||
} | ||
return undefined; | ||
}; | ||
} |
@@ -1,8 +0,8 @@ | ||
/// <reference types="node" /> | ||
import { Readable } from 'stream'; | ||
interface FormData extends Readable { | ||
/// <reference types="node" resolution-mode="require"/> | ||
import type { Readable } from 'node:stream'; | ||
type FormData = { | ||
getBoundary: () => string; | ||
getLength: (callback: (error: Error | null, length: number) => void) => void; | ||
} | ||
declare const _default: (body: unknown) => body is FormData; | ||
export default _default; | ||
} & Readable; | ||
export default function isFormData(body: unknown): body is FormData; | ||
export {}; |
@@ -1,4 +0,4 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const is_1 = require("@sindresorhus/is"); | ||
exports.default = (body) => is_1.default.nodeStream(body) && is_1.default.function_(body.getBoundary); | ||
import is from '@sindresorhus/is'; | ||
export default function isFormData(body) { | ||
return is.nodeStream(body) && is.function_(body.getBoundary); | ||
} |
@@ -1,3 +0,3 @@ | ||
import { URL } from 'url'; | ||
export interface URLOptions { | ||
/// <reference types="node" resolution-mode="require"/> | ||
export type URLOptions = { | ||
href?: string; | ||
@@ -12,4 +12,3 @@ protocol?: string; | ||
path?: string; | ||
} | ||
declare const _default: (origin: string, options: URLOptions) => URL; | ||
export default _default; | ||
}; | ||
export default function optionsToUrl(origin: string, options: URLOptions): URL; |
@@ -1,5 +0,1 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/* istanbul ignore file: deprecated */ | ||
const url_1 = require("url"); | ||
const keys = [ | ||
@@ -11,6 +7,5 @@ 'protocol', | ||
'pathname', | ||
'search' | ||
'search', | ||
]; | ||
exports.default = (origin, options) => { | ||
var _a, _b; | ||
export default function optionsToUrl(origin, options) { | ||
if (options.path) { | ||
@@ -34,5 +29,5 @@ if (options.pathname) { | ||
} | ||
origin = `${options.protocol}//${(_b = (_a = options.hostname) !== null && _a !== void 0 ? _a : options.host) !== null && _b !== void 0 ? _b : ''}`; | ||
origin = `${options.protocol}//${options.hostname ?? options.host ?? ''}`; | ||
} | ||
const url = new url_1.URL(origin); | ||
const url = new URL(origin); | ||
if (options.path) { | ||
@@ -55,2 +50,2 @@ const searchIndex = options.path.indexOf('?'); | ||
return url; | ||
}; | ||
} |
@@ -1,3 +0,3 @@ | ||
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
export default function (from: EventEmitter, to: EventEmitter, events: string[]): () => void; | ||
/// <reference types="node" resolution-mode="require"/> | ||
import type { EventEmitter } from 'node:events'; | ||
export default function proxyEvents(from: EventEmitter, to: EventEmitter, events: Readonly<string[]>): () => void; |
@@ -1,17 +0,15 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
function default_1(from, to, events) { | ||
const fns = {}; | ||
export default function proxyEvents(from, to, events) { | ||
const eventFunctions = {}; | ||
for (const event of events) { | ||
fns[event] = (...args) => { | ||
const eventFunction = (...args) => { | ||
to.emit(event, ...args); | ||
}; | ||
from.on(event, fns[event]); | ||
eventFunctions[event] = eventFunction; | ||
from.on(event, eventFunction); | ||
} | ||
return () => { | ||
for (const event of events) { | ||
from.off(event, fns[event]); | ||
for (const [event, eventFunction] of Object.entries(eventFunctions)) { | ||
from.off(event, eventFunction); | ||
} | ||
}; | ||
} | ||
exports.default = default_1; |
@@ -1,11 +0,11 @@ | ||
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
declare type Origin = EventEmitter; | ||
declare type Event = string | symbol; | ||
declare type Fn = (...args: any[]) => void; | ||
interface Unhandler { | ||
/// <reference types="node" resolution-mode="require"/> | ||
import type { EventEmitter } from 'node:events'; | ||
type Origin = EventEmitter; | ||
type Event = string | symbol; | ||
type Fn = (...args: any[]) => void; | ||
type Unhandler = { | ||
once: (origin: Origin, event: Event, fn: Fn) => void; | ||
unhandleAll: () => void; | ||
} | ||
declare const _default: () => Unhandler; | ||
export default _default; | ||
}; | ||
export default function unhandle(): Unhandler; | ||
export {}; |
@@ -1,3 +0,1 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// When attaching listeners, it's very easy to forget about them. | ||
@@ -7,3 +5,3 @@ // Especially if you do error handling and set timeouts. | ||
// use this simple tool which will remove all listeners you have attached. | ||
exports.default = () => { | ||
export default function unhandle() { | ||
const handlers = []; | ||
@@ -21,4 +19,4 @@ return { | ||
handlers.length = 0; | ||
} | ||
}, | ||
}; | ||
}; | ||
} |
@@ -1,3 +0,4 @@ | ||
import { URL, UrlWithStringQuery } from 'url'; | ||
export interface LegacyUrlOptions { | ||
/// <reference types="node" resolution-mode="require"/> | ||
import type { UrlWithStringQuery } from 'node:url'; | ||
export type LegacyUrlOptions = { | ||
protocol: string; | ||
@@ -13,4 +14,3 @@ hostname: string; | ||
auth?: string; | ||
} | ||
declare const _default: (url: URL | UrlWithStringQuery) => LegacyUrlOptions; | ||
export default _default; | ||
}; | ||
export default function urlToOptions(url: URL | UrlWithStringQuery): LegacyUrlOptions; |
@@ -1,5 +0,3 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const is_1 = require("@sindresorhus/is"); | ||
exports.default = (url) => { | ||
import is from '@sindresorhus/is'; | ||
export default function urlToOptions(url) { | ||
// Cast to URL | ||
@@ -9,3 +7,3 @@ url = url; | ||
protocol: url.protocol, | ||
hostname: is_1.default.string(url.hostname) && url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname, | ||
hostname: is.string(url.hostname) && url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname, | ||
host: url.host, | ||
@@ -16,5 +14,5 @@ hash: url.hash, | ||
href: url.href, | ||
path: `${url.pathname || ''}${url.search || ''}` | ||
path: `${url.pathname || ''}${url.search || ''}`, | ||
}; | ||
if (is_1.default.string(url.port) && url.port.length > 0) { | ||
if (is.string(url.port) && url.port.length > 0) { | ||
options.port = Number(url.port); | ||
@@ -26,2 +24,2 @@ } | ||
return options; | ||
}; | ||
} |
@@ -1,5 +0,15 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
class WeakableMap { | ||
export default class WeakableMap { | ||
constructor() { | ||
Object.defineProperty(this, "weakMap", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "map", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
this.weakMap = new WeakMap(); | ||
@@ -29,2 +39,1 @@ this.map = new Map(); | ||
} | ||
exports.default = WeakableMap; |
@@ -1,5 +0,3 @@ | ||
import { Got, HandlerFunction, InstanceDefaults } from './types'; | ||
export declare const defaultHandler: HandlerFunction; | ||
import type { Got, InstanceDefaults } from './types.js'; | ||
declare const create: (defaults: InstanceDefaults) => Got; | ||
export default create; | ||
export * from './types'; |
@@ -1,31 +0,5 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defaultHandler = void 0; | ||
const is_1 = require("@sindresorhus/is"); | ||
const as_promise_1 = require("./as-promise"); | ||
const create_rejection_1 = require("./as-promise/create-rejection"); | ||
const core_1 = require("./core"); | ||
const deep_freeze_1 = require("./utils/deep-freeze"); | ||
const errors = { | ||
RequestError: as_promise_1.RequestError, | ||
CacheError: as_promise_1.CacheError, | ||
ReadError: as_promise_1.ReadError, | ||
HTTPError: as_promise_1.HTTPError, | ||
MaxRedirectsError: as_promise_1.MaxRedirectsError, | ||
TimeoutError: as_promise_1.TimeoutError, | ||
ParseError: as_promise_1.ParseError, | ||
CancelError: as_promise_1.CancelError, | ||
UnsupportedProtocolError: as_promise_1.UnsupportedProtocolError, | ||
UploadError: as_promise_1.UploadError | ||
}; | ||
import is, { assert } from '@sindresorhus/is'; | ||
import asPromise from './as-promise/index.js'; | ||
import Request from './core/index.js'; | ||
import Options from './core/options.js'; | ||
// The `delay` package weighs 10KB (!) | ||
@@ -35,12 +9,3 @@ const delay = async (ms) => new Promise(resolve => { | ||
}); | ||
const { normalizeArguments } = core_1.default; | ||
const mergeOptions = (...sources) => { | ||
let mergedOptions; | ||
for (const source of sources) { | ||
mergedOptions = normalizeArguments(undefined, source, mergedOptions); | ||
} | ||
return mergedOptions; | ||
}; | ||
const getPromiseOrStream = (options) => options.isStream ? new core_1.default(undefined, options) : as_promise_1.default(options); | ||
const isGotInstance = (value) => ('defaults' in value && 'options' in value.defaults); | ||
const isGotInstance = (value) => is.function_(value); | ||
const aliases = [ | ||
@@ -52,105 +17,79 @@ 'get', | ||
'head', | ||
'delete' | ||
'delete', | ||
]; | ||
exports.defaultHandler = (options, next) => next(options); | ||
const callInitHooks = (hooks, options) => { | ||
if (hooks) { | ||
for (const hook of hooks) { | ||
hook(options); | ||
} | ||
} | ||
}; | ||
const create = (defaults) => { | ||
// Proxy properties from next handlers | ||
defaults._rawHandlers = defaults.handlers; | ||
defaults.handlers = defaults.handlers.map(fn => ((options, next) => { | ||
// This will be assigned by assigning result | ||
let root; | ||
const result = fn(options, newOptions => { | ||
root = next(newOptions); | ||
return root; | ||
}); | ||
if (result !== root && !options.isStream && root) { | ||
const typedResult = result; | ||
const { then: promiseThen, catch: promiseCatch, finally: promiseFianlly } = typedResult; | ||
Object.setPrototypeOf(typedResult, Object.getPrototypeOf(root)); | ||
Object.defineProperties(typedResult, Object.getOwnPropertyDescriptors(root)); | ||
// These should point to the new promise | ||
// eslint-disable-next-line promise/prefer-await-to-then | ||
typedResult.then = promiseThen; | ||
typedResult.catch = promiseCatch; | ||
typedResult.finally = promiseFianlly; | ||
} | ||
return result; | ||
})); | ||
defaults = { | ||
options: new Options(undefined, undefined, defaults.options), | ||
handlers: [...defaults.handlers], | ||
mutableDefaults: defaults.mutableDefaults, | ||
}; | ||
Object.defineProperty(defaults, 'mutableDefaults', { | ||
enumerable: true, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
// Got interface | ||
const got = ((url, options = {}, _defaults) => { | ||
var _a, _b; | ||
const got = ((url, options, defaultOptions = defaults.options) => { | ||
const request = new Request(url, options, defaultOptions); | ||
let promise; | ||
const lastHandler = (normalized) => { | ||
// Note: `options` is `undefined` when `new Options(...)` fails | ||
request.options = normalized; | ||
request._noPipe = !normalized.isStream; | ||
void request.flush(); | ||
if (normalized.isStream) { | ||
return request; | ||
} | ||
if (!promise) { | ||
promise = asPromise(request); | ||
} | ||
return promise; | ||
}; | ||
let iteration = 0; | ||
const iterateHandlers = (newOptions) => { | ||
return defaults.handlers[iteration++](newOptions, iteration === defaults.handlers.length ? getPromiseOrStream : iterateHandlers); | ||
const handler = defaults.handlers[iteration++] ?? lastHandler; | ||
const result = handler(newOptions, iterateHandlers); | ||
if (is.promise(result) && !request.options.isStream) { | ||
if (!promise) { | ||
promise = asPromise(request); | ||
} | ||
if (result !== promise) { | ||
const descriptors = Object.getOwnPropertyDescriptors(promise); | ||
for (const key in descriptors) { | ||
if (key in result) { | ||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete | ||
delete descriptors[key]; | ||
} | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
Object.defineProperties(result, descriptors); | ||
result.cancel = promise.cancel; | ||
} | ||
} | ||
return result; | ||
}; | ||
// TODO: Remove this in Got 12. | ||
if (is_1.default.plainObject(url)) { | ||
const mergedOptions = { | ||
...url, | ||
...options | ||
}; | ||
core_1.setNonEnumerableProperties([url, options], mergedOptions); | ||
options = mergedOptions; | ||
url = undefined; | ||
} | ||
try { | ||
// Call `init` hooks | ||
let initHookError; | ||
try { | ||
callInitHooks(defaults.options.hooks.init, options); | ||
callInitHooks((_a = options.hooks) === null || _a === void 0 ? void 0 : _a.init, options); | ||
} | ||
catch (error) { | ||
initHookError = error; | ||
} | ||
// Normalize options & call handlers | ||
const normalizedOptions = normalizeArguments(url, options, _defaults !== null && _defaults !== void 0 ? _defaults : defaults.options); | ||
normalizedOptions[core_1.kIsNormalizedAlready] = true; | ||
if (initHookError) { | ||
throw new as_promise_1.RequestError(initHookError.message, initHookError, normalizedOptions); | ||
} | ||
return iterateHandlers(normalizedOptions); | ||
} | ||
catch (error) { | ||
if (options.isStream) { | ||
throw error; | ||
} | ||
else { | ||
return create_rejection_1.default(error, defaults.options.hooks.beforeError, (_b = options.hooks) === null || _b === void 0 ? void 0 : _b.beforeError); | ||
} | ||
} | ||
return iterateHandlers(request.options); | ||
}); | ||
got.extend = (...instancesOrOptions) => { | ||
const optionsArray = [defaults.options]; | ||
let handlers = [...defaults._rawHandlers]; | ||
let isMutableDefaults; | ||
const options = new Options(undefined, undefined, defaults.options); | ||
const handlers = [...defaults.handlers]; | ||
let mutableDefaults; | ||
for (const value of instancesOrOptions) { | ||
if (isGotInstance(value)) { | ||
optionsArray.push(value.defaults.options); | ||
handlers.push(...value.defaults._rawHandlers); | ||
isMutableDefaults = value.defaults.mutableDefaults; | ||
options.merge(value.defaults.options); | ||
handlers.push(...value.defaults.handlers); | ||
mutableDefaults = value.defaults.mutableDefaults; | ||
} | ||
else { | ||
optionsArray.push(value); | ||
if ('handlers' in value) { | ||
options.merge(value); | ||
if (value.handlers) { | ||
handlers.push(...value.handlers); | ||
} | ||
isMutableDefaults = value.mutableDefaults; | ||
mutableDefaults = value.mutableDefaults; | ||
} | ||
} | ||
handlers = handlers.filter(handler => handler !== exports.defaultHandler); | ||
if (handlers.length === 0) { | ||
handlers.push(exports.defaultHandler); | ||
} | ||
return create({ | ||
options: mergeOptions(...optionsArray), | ||
options, | ||
handlers, | ||
mutableDefaults: Boolean(isMutableDefaults) | ||
mutableDefaults: Boolean(mutableDefaults), | ||
}); | ||
@@ -160,12 +99,13 @@ }; | ||
const paginateEach = (async function* (url, options) { | ||
// TODO: Remove this `@ts-expect-error` when upgrading to TypeScript 4. | ||
// Error: Argument of type 'Merge<Options, PaginationOptions<T, R>> | undefined' is not assignable to parameter of type 'Options | undefined'. | ||
// @ts-expect-error | ||
let normalizedOptions = normalizeArguments(url, options, defaults.options); | ||
let normalizedOptions = new Options(url, options, defaults.options); | ||
normalizedOptions.resolveBodyOnly = false; | ||
const pagination = normalizedOptions.pagination; | ||
if (!is_1.default.object(pagination)) { | ||
throw new TypeError('`options.pagination` must be implemented'); | ||
} | ||
const all = []; | ||
const { pagination } = normalizedOptions; | ||
assert.function_(pagination.transform); | ||
assert.function_(pagination.shouldContinue); | ||
assert.function_(pagination.filter); | ||
assert.function_(pagination.paginate); | ||
assert.number(pagination.countLimit); | ||
assert.number(pagination.requestLimit); | ||
assert.number(pagination.backoff); | ||
const allItems = []; | ||
let { countLimit } = pagination; | ||
@@ -178,12 +118,11 @@ let numberOfRequests = 0; | ||
} | ||
// @ts-expect-error FIXME! | ||
// TODO: Throw when result is not an instance of Response | ||
// eslint-disable-next-line no-await-in-loop | ||
const result = (await got(undefined, undefined, normalizedOptions)); | ||
const response = (await got(undefined, undefined, normalizedOptions)); | ||
// eslint-disable-next-line no-await-in-loop | ||
const parsed = await pagination.transform(result); | ||
const current = []; | ||
const parsed = await pagination.transform(response); | ||
const currentItems = []; | ||
assert.array(parsed); | ||
for (const item of parsed) { | ||
if (pagination.filter(item, all, current)) { | ||
if (!pagination.shouldContinue(item, all, current)) { | ||
if (pagination.filter({ item, currentItems, allItems })) { | ||
if (!pagination.shouldContinue({ item, currentItems, allItems })) { | ||
return; | ||
@@ -193,5 +132,5 @@ } | ||
if (pagination.stackAllItems) { | ||
all.push(item); | ||
allItems.push(item); | ||
} | ||
current.push(item); | ||
currentItems.push(item); | ||
if (--countLimit <= 0) { | ||
@@ -202,11 +141,20 @@ return; | ||
} | ||
const optionsToMerge = pagination.paginate(result, all, current); | ||
const optionsToMerge = pagination.paginate({ | ||
response, | ||
currentItems, | ||
allItems, | ||
}); | ||
if (optionsToMerge === false) { | ||
return; | ||
} | ||
if (optionsToMerge === result.request.options) { | ||
normalizedOptions = result.request.options; | ||
if (optionsToMerge === response.request.options) { | ||
normalizedOptions = response.request.options; | ||
} | ||
else if (optionsToMerge !== undefined) { | ||
normalizedOptions = normalizeArguments(undefined, optionsToMerge, normalizedOptions); | ||
else { | ||
normalizedOptions.merge(optionsToMerge); | ||
assert.any([is.urlInstance, is.undefined], optionsToMerge.url); | ||
if (optionsToMerge.url !== undefined) { | ||
normalizedOptions.prefixUrl = ''; | ||
normalizedOptions.url = optionsToMerge.url; | ||
} | ||
} | ||
@@ -231,17 +179,16 @@ numberOfRequests++; | ||
got[method] = ((url, options) => got(url, { ...options, method })); | ||
got.stream[method] = ((url, options) => { | ||
return got(url, { ...options, method, isStream: true }); | ||
}); | ||
got.stream[method] = ((url, options) => got(url, { ...options, method, isStream: true })); | ||
} | ||
Object.assign(got, errors); | ||
if (!defaults.mutableDefaults) { | ||
Object.freeze(defaults.handlers); | ||
defaults.options.freeze(); | ||
} | ||
Object.defineProperty(got, 'defaults', { | ||
value: defaults.mutableDefaults ? defaults : deep_freeze_1.default(defaults), | ||
writable: defaults.mutableDefaults, | ||
configurable: defaults.mutableDefaults, | ||
enumerable: true | ||
value: defaults, | ||
writable: false, | ||
configurable: false, | ||
enumerable: true, | ||
}); | ||
got.mergeOptions = mergeOptions; | ||
return got; | ||
}; | ||
exports.default = create; | ||
__exportStar(require("./types"), exports); | ||
export default create; |
@@ -1,4 +0,15 @@ | ||
declare const got: import("./types").Got; | ||
declare const got: import("./types.js").Got; | ||
export default got; | ||
export * from './create'; | ||
export * from './as-promise'; | ||
export { got }; | ||
export { default as Options } from './core/options.js'; | ||
export * from './core/options.js'; | ||
export * from './core/response.js'; | ||
export type { default as Request } from './core/index.js'; | ||
export * from './core/index.js'; | ||
export * from './core/errors.js'; | ||
export type { Delays } from './core/timed-out.js'; | ||
export { default as calculateRetryDelay } from './core/calculate-retry-delay.js'; | ||
export * from './as-promise/types.js'; | ||
export * from './types.js'; | ||
export { default as create } from './create.js'; | ||
export { default as parseLinkHeader } from './core/parse-link-header.js'; |
@@ -1,132 +0,20 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const url_1 = require("url"); | ||
const create_1 = require("./create"); | ||
import create from './create.js'; | ||
import Options from './core/options.js'; | ||
const defaults = { | ||
options: { | ||
method: 'GET', | ||
retry: { | ||
limit: 2, | ||
methods: [ | ||
'GET', | ||
'PUT', | ||
'HEAD', | ||
'DELETE', | ||
'OPTIONS', | ||
'TRACE' | ||
], | ||
statusCodes: [ | ||
408, | ||
413, | ||
429, | ||
500, | ||
502, | ||
503, | ||
504, | ||
521, | ||
522, | ||
524 | ||
], | ||
errorCodes: [ | ||
'ETIMEDOUT', | ||
'ECONNRESET', | ||
'EADDRINUSE', | ||
'ECONNREFUSED', | ||
'EPIPE', | ||
'ENOTFOUND', | ||
'ENETUNREACH', | ||
'EAI_AGAIN' | ||
], | ||
maxRetryAfter: undefined, | ||
calculateDelay: ({ computedValue }) => computedValue | ||
}, | ||
timeout: {}, | ||
headers: { | ||
'user-agent': 'got (https://github.com/sindresorhus/got)' | ||
}, | ||
hooks: { | ||
init: [], | ||
beforeRequest: [], | ||
beforeRedirect: [], | ||
beforeRetry: [], | ||
beforeError: [], | ||
afterResponse: [] | ||
}, | ||
cache: undefined, | ||
dnsCache: undefined, | ||
decompress: true, | ||
throwHttpErrors: true, | ||
followRedirect: true, | ||
isStream: false, | ||
responseType: 'text', | ||
resolveBodyOnly: false, | ||
maxRedirects: 10, | ||
prefixUrl: '', | ||
methodRewriting: true, | ||
ignoreInvalidCookies: false, | ||
context: {}, | ||
// TODO: Set this to `true` when Got 12 gets released | ||
http2: false, | ||
allowGetBody: false, | ||
https: undefined, | ||
pagination: { | ||
transform: (response) => { | ||
if (response.request.options.responseType === 'json') { | ||
return response.body; | ||
} | ||
return JSON.parse(response.body); | ||
}, | ||
paginate: response => { | ||
if (!Reflect.has(response.headers, 'link')) { | ||
return false; | ||
} | ||
const items = response.headers.link.split(','); | ||
let next; | ||
for (const item of items) { | ||
const parsed = item.split(';'); | ||
if (parsed[1].includes('next')) { | ||
next = parsed[0].trimStart().trim(); | ||
next = next.slice(1, -1); | ||
break; | ||
} | ||
} | ||
if (next) { | ||
const options = { | ||
url: new url_1.URL(next) | ||
}; | ||
return options; | ||
} | ||
return false; | ||
}, | ||
filter: () => true, | ||
shouldContinue: () => true, | ||
countLimit: Infinity, | ||
backoff: 0, | ||
requestLimit: 10000, | ||
stackAllItems: true | ||
}, | ||
parseJson: (text) => JSON.parse(text), | ||
stringifyJson: (object) => JSON.stringify(object), | ||
cacheOptions: {} | ||
}, | ||
handlers: [create_1.defaultHandler], | ||
mutableDefaults: false | ||
options: new Options(), | ||
handlers: [], | ||
mutableDefaults: false, | ||
}; | ||
const got = create_1.default(defaults); | ||
exports.default = got; | ||
// For CommonJS default export support | ||
module.exports = got; | ||
module.exports.default = got; | ||
module.exports.__esModule = true; // Workaround for TS issue: https://github.com/sindresorhus/got/pull/1267 | ||
__exportStar(require("./create"), exports); | ||
__exportStar(require("./as-promise"), exports); | ||
const got = create(defaults); | ||
export default got; | ||
export { got }; | ||
export { default as Options } from './core/options.js'; | ||
export * from './core/options.js'; | ||
export * from './core/response.js'; | ||
export * from './core/index.js'; | ||
export * from './core/errors.js'; | ||
export { default as calculateRetryDelay } from './core/calculate-retry-delay.js'; | ||
export * from './as-promise/types.js'; | ||
export * from './types.js'; | ||
export { default as create } from './create.js'; | ||
export { default as parseLinkHeader } from './core/parse-link-header.js'; |
@@ -1,16 +0,19 @@ | ||
/// <reference types="node" /> | ||
import { URL } from 'url'; | ||
import { CancelError } from 'p-cancelable'; | ||
import { CancelableRequest, Response, Options, NormalizedOptions, Defaults as DefaultOptions, PaginationOptions, ParseError, RequestError, CacheError, ReadError, HTTPError, MaxRedirectsError, TimeoutError, UnsupportedProtocolError, UploadError } from './as-promise'; | ||
import Request from './core'; | ||
declare type Except<ObjectType, KeysType extends keyof ObjectType> = Pick<ObjectType, Exclude<keyof ObjectType, KeysType>>; | ||
declare type Merge<FirstType, SecondType> = Except<FirstType, Extract<keyof FirstType, keyof SecondType>> & SecondType; | ||
/// <reference types="node" resolution-mode="require"/> | ||
/// <reference types="node" resolution-mode="require"/> | ||
import type { Buffer } from 'node:buffer'; | ||
import type { CancelableRequest } from './as-promise/types.js'; | ||
import type { Response } from './core/response.js'; | ||
import type Options from './core/options.js'; | ||
import type { PaginationOptions, OptionsInit } from './core/options.js'; | ||
import type Request from './core/index.js'; | ||
type Except<ObjectType, KeysType extends keyof ObjectType> = Pick<ObjectType, Exclude<keyof ObjectType, KeysType>>; | ||
type Merge<FirstType, SecondType> = Except<FirstType, Extract<keyof FirstType, keyof SecondType>> & SecondType; | ||
/** | ||
Defaults for each Got instance. | ||
*/ | ||
export interface InstanceDefaults { | ||
export type InstanceDefaults = { | ||
/** | ||
An object containing the default options of Got. | ||
An object containing the default options of Got. | ||
*/ | ||
options: DefaultOptions; | ||
options: Options; | ||
/** | ||
@@ -31,8 +34,7 @@ An array of functions. You execute them directly by calling `got()`. | ||
mutableDefaults: boolean; | ||
_rawHandlers?: HandlerFunction[]; | ||
} | ||
}; | ||
/** | ||
A Request object returned by calling Got, or any of the Got HTTP alias request functions. | ||
*/ | ||
export declare type GotReturn = Request | CancelableRequest; | ||
export type GotReturn = Request | CancelableRequest; | ||
/** | ||
@@ -42,7 +44,7 @@ A function to handle options and returns a Request object. | ||
*/ | ||
export declare type HandlerFunction = <T extends GotReturn>(options: NormalizedOptions, next: (options: NormalizedOptions) => T) => T | Promise<T>; | ||
export type HandlerFunction = <T extends GotReturn>(options: Options, next: (options: Options) => T) => T | Promise<T>; | ||
/** | ||
The options available for `got.extend()`. | ||
*/ | ||
export interface ExtendOptions extends Options { | ||
export type ExtendOptions = { | ||
/** | ||
@@ -63,4 +65,4 @@ An array of functions. You execute them directly by calling `got()`. | ||
mutableDefaults?: boolean; | ||
} | ||
export declare type OptionsOfTextResponseBody = Merge<Options, { | ||
} & OptionsInit; | ||
export type OptionsOfTextResponseBody = Merge<OptionsInit, { | ||
isStream?: false; | ||
@@ -70,3 +72,3 @@ resolveBodyOnly?: false; | ||
}>; | ||
export declare type OptionsOfJSONResponseBody = Merge<Options, { | ||
export type OptionsOfJSONResponseBody = Merge<OptionsInit, { | ||
isStream?: false; | ||
@@ -76,3 +78,3 @@ resolveBodyOnly?: false; | ||
}>; | ||
export declare type OptionsOfBufferResponseBody = Merge<Options, { | ||
export type OptionsOfBufferResponseBody = Merge<OptionsInit, { | ||
isStream?: false; | ||
@@ -82,19 +84,29 @@ resolveBodyOnly?: false; | ||
}>; | ||
export declare type OptionsOfUnknownResponseBody = Merge<Options, { | ||
export type OptionsOfUnknownResponseBody = Merge<OptionsInit, { | ||
isStream?: false; | ||
resolveBodyOnly?: false; | ||
}>; | ||
export declare type StrictOptions = Except<Options, 'isStream' | 'responseType' | 'resolveBodyOnly'>; | ||
export declare type StreamOptions = Merge<Options, { | ||
export type StrictOptions = Except<OptionsInit, 'isStream' | 'responseType' | 'resolveBodyOnly'>; | ||
export type StreamOptions = Merge<OptionsInit, { | ||
isStream?: true; | ||
}>; | ||
declare type ResponseBodyOnly = { | ||
type ResponseBodyOnly = { | ||
resolveBodyOnly: true; | ||
}; | ||
export declare type OptionsWithPagination<T = unknown, R = unknown> = Merge<Options, PaginationOptions<T, R>>; | ||
export type OptionsWithPagination<T = unknown, R = unknown> = Merge<OptionsInit, { | ||
pagination?: PaginationOptions<T, R>; | ||
}>; | ||
/** | ||
An instance of `got.paginate`. | ||
*/ | ||
export interface GotPaginate { | ||
export type GotPaginate = { | ||
/** | ||
Same as `GotPaginate.each`. | ||
*/ | ||
<T, R = unknown>(url: string | URL, options?: OptionsWithPagination<T, R>): AsyncIterableIterator<T>; | ||
/** | ||
Same as `GotPaginate.each`. | ||
*/ | ||
<T, R = unknown>(options?: OptionsWithPagination<T, R>): AsyncIterableIterator<T>; | ||
/** | ||
Returns an async iterator. | ||
@@ -106,15 +118,15 @@ | ||
``` | ||
(async () => { | ||
const countLimit = 10; | ||
import got from 'got'; | ||
const pagination = got.paginate('https://api.github.com/repos/sindresorhus/got/commits', { | ||
pagination: {countLimit} | ||
}); | ||
const countLimit = 10; | ||
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`); | ||
const pagination = got.paginate('https://api.github.com/repos/sindresorhus/got/commits', { | ||
pagination: {countLimit} | ||
}); | ||
for await (const commitData of pagination) { | ||
console.log(commitData.commit.message); | ||
} | ||
})(); | ||
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`); | ||
for await (const commitData of pagination) { | ||
console.log(commitData.commit.message); | ||
} | ||
``` | ||
@@ -130,25 +142,17 @@ */ | ||
``` | ||
(async () => { | ||
const countLimit = 10; | ||
import got from 'got'; | ||
const results = await got.paginate.all('https://api.github.com/repos/sindresorhus/got/commits', { | ||
pagination: {countLimit} | ||
}); | ||
const countLimit = 10; | ||
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`); | ||
console.log(results); | ||
})(); | ||
const results = await got.paginate.all('https://api.github.com/repos/sindresorhus/got/commits', { | ||
pagination: {countLimit} | ||
}); | ||
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`); | ||
console.log(results); | ||
``` | ||
*/ | ||
all: (<T, R = unknown>(url: string | URL, options?: OptionsWithPagination<T, R>) => Promise<T[]>) & (<T, R = unknown>(options?: OptionsWithPagination<T, R>) => Promise<T[]>); | ||
/** | ||
Same as `GotPaginate.each`. | ||
*/ | ||
<T, R = unknown>(url: string | URL, options?: OptionsWithPagination<T, R>): AsyncIterableIterator<T>; | ||
/** | ||
Same as `GotPaginate.each`. | ||
*/ | ||
<T, R = unknown>(options?: OptionsWithPagination<T, R>): AsyncIterableIterator<T>; | ||
} | ||
export interface GotRequestFunction { | ||
}; | ||
export type GotRequestFunction = { | ||
(url: string | URL, options?: OptionsOfTextResponseBody): CancelableRequest<Response<string>>; | ||
@@ -168,31 +172,29 @@ <T>(url: string | URL, options?: OptionsOfJSONResponseBody): CancelableRequest<Response<T>>; | ||
(options: (Merge<OptionsOfBufferResponseBody, ResponseBodyOnly>)): CancelableRequest<Buffer>; | ||
(url: string | URL, options?: Merge<Options, { | ||
(url: string | URL, options?: Merge<OptionsInit, { | ||
isStream: true; | ||
}>): Request; | ||
(options: Merge<Options, { | ||
(options: Merge<OptionsInit, { | ||
isStream: true; | ||
}>): Request; | ||
(url: string | URL, options?: Options): CancelableRequest | Request; | ||
(options: Options): CancelableRequest | Request; | ||
} | ||
(url: string | URL, options?: OptionsInit): CancelableRequest | Request; | ||
(options: OptionsInit): CancelableRequest | Request; | ||
(url: undefined, options: undefined, defaults: Options): CancelableRequest | Request; | ||
}; | ||
/** | ||
All available HTTP request methods provided by Got. | ||
*/ | ||
export declare type HTTPAlias = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete'; | ||
interface GotStreamFunction { | ||
(url: string | URL, options?: Merge<Options, { | ||
isStream?: true; | ||
}>): Request; | ||
(options?: Merge<Options, { | ||
isStream?: true; | ||
}>): Request; | ||
} | ||
export type HTTPAlias = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete'; | ||
type GotStreamFunction = ((url?: string | URL, options?: Merge<OptionsInit, { | ||
isStream?: true; | ||
}>) => Request) & ((options?: Merge<OptionsInit, { | ||
isStream?: true; | ||
}>) => Request); | ||
/** | ||
An instance of `got.stream()`. | ||
*/ | ||
export declare type GotStream = GotStreamFunction & Record<HTTPAlias, GotStreamFunction>; | ||
export type GotStream = GotStreamFunction & Record<HTTPAlias, GotStreamFunction>; | ||
/** | ||
An instance of `got`. | ||
*/ | ||
export interface Got extends Record<HTTPAlias, GotRequestFunction>, GotRequestFunction { | ||
export type Got = { | ||
/** | ||
@@ -217,15 +219,15 @@ Sets `options.isStream` to `true`. | ||
``` | ||
(async () => { | ||
const countLimit = 10; | ||
import got from 'got'; | ||
const pagination = got.paginate('https://api.github.com/repos/sindresorhus/got/commits', { | ||
pagination: {countLimit} | ||
}); | ||
const countLimit = 10; | ||
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`); | ||
const pagination = got.paginate('https://api.github.com/repos/sindresorhus/got/commits', { | ||
pagination: {countLimit} | ||
}); | ||
for await (const commitData of pagination) { | ||
console.log(commitData.commit.message); | ||
} | ||
})(); | ||
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`); | ||
for await (const commitData of pagination) { | ||
console.log(commitData.commit.message); | ||
} | ||
``` | ||
@@ -239,48 +241,2 @@ */ | ||
/** | ||
An error to be thrown when a cache method fails. | ||
For example, if the database goes down or there's a filesystem error. | ||
*/ | ||
CacheError: typeof CacheError; | ||
/** | ||
An error to be thrown when a request fails. | ||
Contains a `code` property with error class code, like `ECONNREFUSED`. | ||
*/ | ||
RequestError: typeof RequestError; | ||
/** | ||
An error to be thrown when reading from response stream fails. | ||
*/ | ||
ReadError: typeof ReadError; | ||
/** | ||
An error to be thrown when server response code is 2xx, and parsing body fails. | ||
Includes a `response` property. | ||
*/ | ||
ParseError: typeof ParseError; | ||
/** | ||
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. | ||
Includes a `response` property. | ||
*/ | ||
HTTPError: typeof HTTPError; | ||
/** | ||
An error to be thrown when the server redirects you more than ten times. | ||
Includes a `response` property. | ||
*/ | ||
MaxRedirectsError: typeof MaxRedirectsError; | ||
/** | ||
An error to be thrown when given an unsupported protocol. | ||
*/ | ||
UnsupportedProtocolError: typeof UnsupportedProtocolError; | ||
/** | ||
An error to be thrown when the request is aborted due to a timeout. | ||
Includes an `event` and `timings` property. | ||
*/ | ||
TimeoutError: typeof TimeoutError; | ||
/** | ||
An error to be thrown when the request body is a stream and an error occurs while reading from that stream. | ||
*/ | ||
UploadError: typeof UploadError; | ||
/** | ||
An error to be thrown when the request is aborted with `.cancel()`. | ||
*/ | ||
CancelError: typeof CancelError; | ||
/** | ||
Configure a new `got` instance with default `options`. | ||
@@ -297,3 +253,5 @@ The `options` are merged with the parent instance's `defaults.options` using `got.mergeOptions`. | ||
@example | ||
```js | ||
``` | ||
import got from 'got'; | ||
const client = got.extend({ | ||
@@ -315,41 +273,3 @@ prefixUrl: 'https://example.com', | ||
extend: (...instancesOrOptions: Array<Got | ExtendOptions>) => Got; | ||
/** | ||
Merges multiple `got` instances into the parent. | ||
*/ | ||
mergeInstances: (parent: Got, ...instances: Got[]) => Got; | ||
/** | ||
Extends parent options. | ||
Avoid using [object spread](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals) as it doesn't work recursively. | ||
Options are deeply merged to a new object. The value of each key is determined as follows: | ||
- If the new property is not defined, the old value is used. | ||
- If the new property is explicitly set to `undefined`: | ||
- If the parent property is a plain `object`, the parent value is deeply cloned. | ||
- Otherwise, `undefined` is used. | ||
- If the parent value is an instance of `URLSearchParams`: | ||
- If the new value is a `string`, an `object` or an instance of `URLSearchParams`, a new `URLSearchParams` instance is created. | ||
The values are merged using [`urlSearchParams.append(key, value)`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/append). | ||
The keys defined in the new value override the keys defined in the parent value. | ||
- Otherwise, the only available value is `undefined`. | ||
- If the new property is a plain `object`: | ||
- If the parent property is a plain `object` too, both values are merged recursively into a new `object`. | ||
- Otherwise, only the new value is deeply cloned. | ||
- If the new property is an `Array`, it overwrites the old one with a deep clone of the new property. | ||
- Properties that are not enumerable, such as `context`, `body`, `json`, and `form`, will not be merged. | ||
- Otherwise, the new value is assigned to the key. | ||
**Note:** Only Got options are merged! Custom user options should be defined via [`options.context`](#context). | ||
@example | ||
``` | ||
const a = {headers: {cat: 'meow', wolf: ['bark', 'wrrr']}}; | ||
const b = {headers: {cow: 'moo', wolf: ['auuu']}}; | ||
{...a, ...b} // => {headers: {cow: 'moo', wolf: ['auuu']}} | ||
got.mergeOptions(a, b) // => {headers: {cat: 'meow', cow: 'moo', wolf: ['auuu']}} | ||
``` | ||
*/ | ||
mergeOptions: (...sources: Options[]) => NormalizedOptions; | ||
} | ||
} & Record<HTTPAlias, GotRequestFunction> & GotRequestFunction; | ||
export {}; |
@@ -1,2 +0,1 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
export {}; |
137
package.json
{ | ||
"name": "got", | ||
"version": "11.8.2", | ||
"version": "13.0.0", | ||
"description": "Human-friendly and powerful HTTP request library for Node.js", | ||
@@ -8,8 +8,12 @@ "license": "MIT", | ||
"funding": "https://github.com/sindresorhus/got?sponsor=1", | ||
"main": "dist/source", | ||
"type": "module", | ||
"exports": { | ||
"types": "./dist/source/index.d.ts", | ||
"default": "./dist/source/index.js" | ||
}, | ||
"engines": { | ||
"node": ">=10.19.0" | ||
"node": ">=16" | ||
}, | ||
"scripts": { | ||
"test": "xo && npm run build && nyc --reporter=html --reporter=text ava", | ||
"test": "xo && tsc --noEmit && ava", | ||
"release": "np", | ||
@@ -47,53 +51,59 @@ "build": "del-cli dist && tsc", | ||
"dependencies": { | ||
"@sindresorhus/is": "^4.0.0", | ||
"@szmarczak/http-timer": "^4.0.5", | ||
"@types/cacheable-request": "^6.0.1", | ||
"@types/responselike": "^1.0.0", | ||
"cacheable-lookup": "^5.0.3", | ||
"cacheable-request": "^7.0.1", | ||
"@sindresorhus/is": "^5.2.0", | ||
"@szmarczak/http-timer": "^5.0.1", | ||
"cacheable-lookup": "^7.0.0", | ||
"cacheable-request": "^10.2.8", | ||
"decompress-response": "^6.0.0", | ||
"http2-wrapper": "^1.0.0-beta.5.2", | ||
"lowercase-keys": "^2.0.0", | ||
"p-cancelable": "^2.0.0", | ||
"responselike": "^2.0.0" | ||
"form-data-encoder": "^2.1.2", | ||
"get-stream": "^6.0.1", | ||
"http2-wrapper": "^2.1.10", | ||
"lowercase-keys": "^3.0.0", | ||
"p-cancelable": "^3.0.0", | ||
"responselike": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"@ava/typescript": "^1.1.1", | ||
"@sindresorhus/tsconfig": "^0.7.0", | ||
"@sinonjs/fake-timers": "^6.0.1", | ||
"@types/benchmark": "^1.0.33", | ||
"@types/express": "^4.17.7", | ||
"@types/node": "^14.14.0", | ||
"@types/node-fetch": "^2.5.7", | ||
"@types/pem": "^1.9.5", | ||
"@types/pify": "^3.0.2", | ||
"@types/request": "^2.48.5", | ||
"@types/sinon": "^9.0.5", | ||
"@types/tough-cookie": "^4.0.0", | ||
"ava": "^3.11.1", | ||
"axios": "^0.20.0", | ||
"@hapi/bourne": "^3.0.0", | ||
"@sindresorhus/tsconfig": "^3.0.1", | ||
"@sinonjs/fake-timers": "^10.0.2", | ||
"@types/benchmark": "^2.1.2", | ||
"@types/express": "^4.17.17", | ||
"@types/node": "^18.14.5", | ||
"@types/pem": "^1.9.6", | ||
"@types/pify": "^5.0.1", | ||
"@types/readable-stream": "^2.3.13", | ||
"@types/request": "^2.48.8", | ||
"@types/sinon": "^10.0.11", | ||
"@types/sinonjs__fake-timers": "^8.1.1", | ||
"@types/tough-cookie": "^4.0.1", | ||
"ava": "^5.2.0", | ||
"axios": "^0.27.2", | ||
"benchmark": "^2.1.4", | ||
"coveralls": "^3.1.0", | ||
"bluebird": "^3.7.2", | ||
"body-parser": "^1.20.2", | ||
"create-cert": "^1.0.6", | ||
"create-test-server": "^3.0.1", | ||
"del-cli": "^3.0.1", | ||
"delay": "^4.4.0", | ||
"express": "^4.17.1", | ||
"form-data": "^3.0.0", | ||
"get-stream": "^6.0.0", | ||
"nock": "^13.0.4", | ||
"node-fetch": "^2.6.0", | ||
"np": "^6.4.0", | ||
"del-cli": "^5.0.0", | ||
"delay": "^5.0.0", | ||
"express": "^4.17.3", | ||
"form-data": "^4.0.0", | ||
"formdata-node": "^5.0.0", | ||
"nock": "^13.3.0", | ||
"node-fetch": "^3.2.3", | ||
"np": "^7.6.0", | ||
"nyc": "^15.1.0", | ||
"p-event": "^4.2.0", | ||
"pem": "^1.14.4", | ||
"pify": "^5.0.0", | ||
"sinon": "^9.0.3", | ||
"p-event": "^5.0.1", | ||
"pem": "^1.14.6", | ||
"pify": "^6.0.0", | ||
"readable-stream": "^4.2.0", | ||
"request": "^2.88.2", | ||
"sinon": "^15.0.1", | ||
"slow-stream": "0.0.4", | ||
"tempy": "^1.0.0", | ||
"to-readable-stream": "^2.1.0", | ||
"tough-cookie": "^4.0.0", | ||
"typescript": "4.0.3", | ||
"xo": "^0.34.1" | ||
"tempy": "^3.0.0", | ||
"then-busboy": "^5.2.1", | ||
"tough-cookie": "4.1.2", | ||
"ts-node": "^10.8.2", | ||
"type-fest": "^3.6.1", | ||
"typescript": "^5.0.4", | ||
"xo": "^0.54.2" | ||
}, | ||
"types": "dist/source", | ||
"sideEffects": false, | ||
@@ -105,9 +115,15 @@ "ava": { | ||
"timeout": "1m", | ||
"typescript": { | ||
"rewritePaths": { | ||
"test/": "dist/test/" | ||
} | ||
} | ||
"extensions": { | ||
"ts": "module" | ||
}, | ||
"nodeArguments": [ | ||
"--loader=ts-node/esm" | ||
] | ||
}, | ||
"nyc": { | ||
"reporter": [ | ||
"text", | ||
"html", | ||
"lcov" | ||
], | ||
"extension": [ | ||
@@ -126,6 +142,15 @@ ".ts" | ||
"@typescript-eslint/no-empty-function": "off", | ||
"node/prefer-global/url": "off", | ||
"node/prefer-global/url-search-params": "off", | ||
"import/no-anonymous-default-export": "off", | ||
"@typescript-eslint/no-implicit-any-catch": "off" | ||
"n/no-deprecated-api": "off", | ||
"@typescript-eslint/no-implicit-any-catch": "off", | ||
"ava/assertion-arguments": "off", | ||
"@typescript-eslint/no-unsafe-member-access": "off", | ||
"@typescript-eslint/no-unsafe-return": "off", | ||
"@typescript-eslint/no-unsafe-assignment": "off", | ||
"@typescript-eslint/no-unsafe-call": "off", | ||
"@typescript-eslint/await-thenable": "off", | ||
"@typescript-eslint/no-redundant-type-constituents": "off", | ||
"@typescript-eslint/no-unsafe-argument": "off", | ||
"@typescript-eslint/promise-function-async": "off", | ||
"no-lone-blocks": "off", | ||
"unicorn/no-await-expression-member": "off" | ||
} | ||
@@ -132,0 +157,0 @@ }, |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 4 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
5493
1
1
Yes
248053
43
45
528
+ Addedform-data-encoder@^2.1.2
+ Addedget-stream@^6.0.1
+ Added@sindresorhus/is@5.6.0(transitive)
+ Added@szmarczak/http-timer@5.0.1(transitive)
+ Addedcacheable-lookup@7.0.0(transitive)
+ Addedcacheable-request@10.2.14(transitive)
+ Addedform-data-encoder@2.1.4(transitive)
+ Addedget-stream@6.0.1(transitive)
+ Addedhttp2-wrapper@2.2.1(transitive)
+ Addedlowercase-keys@3.0.0(transitive)
+ Addedmimic-response@4.0.0(transitive)
+ Addednormalize-url@8.0.1(transitive)
+ Addedp-cancelable@3.0.0(transitive)
+ Addedresponselike@3.0.0(transitive)
- Removed@types/cacheable-request@^6.0.1
- Removed@types/responselike@^1.0.0
- Removed@sindresorhus/is@4.6.0(transitive)
- Removed@szmarczak/http-timer@4.0.6(transitive)
- Removed@types/cacheable-request@6.0.3(transitive)
- Removed@types/keyv@3.1.4(transitive)
- Removed@types/node@22.8.1(transitive)
- Removed@types/responselike@1.0.3(transitive)
- Removedcacheable-lookup@5.0.4(transitive)
- Removedcacheable-request@7.0.4(transitive)
- Removedclone-response@1.0.3(transitive)
- Removedend-of-stream@1.4.4(transitive)
- Removedget-stream@5.2.0(transitive)
- Removedhttp2-wrapper@1.0.3(transitive)
- Removedlowercase-keys@2.0.0(transitive)
- Removedmimic-response@1.0.1(transitive)
- Removednormalize-url@6.1.0(transitive)
- Removedonce@1.4.0(transitive)
- Removedp-cancelable@2.1.1(transitive)
- Removedpump@3.0.2(transitive)
- Removedresponselike@2.0.1(transitive)
- Removedundici-types@6.19.8(transitive)
- Removedwrappy@1.0.2(transitive)
Updated@sindresorhus/is@^5.2.0
Updated@szmarczak/http-timer@^5.0.1
Updatedcacheable-lookup@^7.0.0
Updatedcacheable-request@^10.2.8
Updatedhttp2-wrapper@^2.1.10
Updatedlowercase-keys@^3.0.0
Updatedp-cancelable@^3.0.0
Updatedresponselike@^3.0.0