Comparing version 10.0.0-alpha.3.2 to 10.0.0-beta.1
@@ -1,3 +0,2 @@ | ||
import { NormalizedOptions, Response, CancelableRequest } from './utils/types'; | ||
export declare const isProxiedSymbol: unique symbol; | ||
export default function asPromise(options: NormalizedOptions): CancelableRequest<Response>; | ||
import { NormalizedOptions, CancelableRequest } from './utils/types'; | ||
export default function asPromise(options: NormalizedOptions): CancelableRequest<any>; |
@@ -7,20 +7,24 @@ "use strict"; | ||
const PCancelable = require("p-cancelable"); | ||
const merge_1 = require("./merge"); | ||
const errors_1 = require("./errors"); | ||
const request_as_event_emitter_1 = require("./request-as-event-emitter"); | ||
const normalize_arguments_1 = require("./normalize-arguments"); | ||
const request_as_event_emitter_1 = require("./request-as-event-emitter"); | ||
exports.isProxiedSymbol = Symbol('proxied'); | ||
const parseBody = (body, responseType, statusCode) => { | ||
if (responseType === 'json' && is_1.default.string(body)) { | ||
return statusCode === 204 ? '' : JSON.parse(body); | ||
} | ||
if (responseType === 'buffer' && is_1.default.string(body)) { | ||
return Buffer.from(body); | ||
} | ||
if (responseType === 'text') { | ||
return String(body); | ||
} | ||
if (responseType === 'default') { | ||
return body; | ||
} | ||
throw new Error(`Failed to parse body of type '${typeof body}' as '${responseType}'`); | ||
}; | ||
function asPromise(options) { | ||
const proxy = new EventEmitter(); | ||
const parseBody = (response) => { | ||
if (options.responseType === 'json') { | ||
response.body = JSON.parse(response.body); | ||
} | ||
else if (options.responseType === 'buffer') { | ||
response.body = Buffer.from(response.body); | ||
} | ||
else if (options.responseType !== 'text' && !is_1.default.falsy(options.responseType)) { | ||
throw new Error(`Failed to parse body of type '${options.responseType}'`); | ||
} | ||
}; | ||
let finalResponse; | ||
// @ts-ignore `.json()`, `.buffer()` and `.text()` are added later | ||
const promise = new PCancelable((resolve, reject, onCancel) => { | ||
@@ -42,7 +46,7 @@ const emitter = request_as_event_emitter_1.default(options); | ||
emitter.on('response', async (response) => { | ||
var _a; | ||
proxy.emit('response', response); | ||
const stream = is_1.default.null_(options.encoding) ? getStream.buffer(response) : getStream(response, { encoding: options.encoding }); | ||
let data; | ||
const streamAsPromise = is_1.default.null_(options.encoding) ? getStream.buffer(response) : getStream(response, { encoding: options.encoding }); | ||
try { | ||
data = await stream; | ||
response.body = await streamAsPromise; | ||
} | ||
@@ -53,15 +57,13 @@ catch (error) { | ||
} | ||
if (response.req && response.req.aborted) { | ||
// Canceled while downloading - will throw a CancelError or TimeoutError | ||
if ((_a = response.req) === null || _a === void 0 ? void 0 : _a.aborted) { | ||
// Canceled while downloading - will throw a `CancelError` or `TimeoutError` error | ||
return; | ||
} | ||
const limitStatusCode = options.followRedirect ? 299 : 399; | ||
response.body = data; | ||
try { | ||
for (const [index, hook] of options.hooks.afterResponse.entries()) { | ||
// @ts-ignore | ||
// eslint-disable-next-line no-await-in-loop | ||
response = await hook(response, updatedOptions => { | ||
updatedOptions = normalize_arguments_1.reNormalizeArguments(merge_1.mergeOptions(options, { | ||
response = await hook(response, async (updatedOptions) => { | ||
updatedOptions = normalize_arguments_1.normalizeArguments(normalize_arguments_1.mergeOptions(options, { | ||
...updatedOptions, | ||
// @ts-ignore TS complaining that it's missing properties, which get merged | ||
retry: { | ||
@@ -77,3 +79,12 @@ calculateDelay: () => 0 | ||
updatedOptions.hooks.afterResponse = options.hooks.afterResponse.slice(0, index); | ||
return asPromise(updatedOptions); | ||
for (const hook of options.hooks.beforeRetry) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await hook(updatedOptions); | ||
} | ||
const promise = asPromise(updatedOptions); | ||
onCancel(() => { | ||
promise.catch(() => { }); | ||
promise.cancel(); | ||
}); | ||
return promise; | ||
}); | ||
@@ -87,14 +98,17 @@ } | ||
const { statusCode } = response; | ||
if (response.body) { | ||
try { | ||
parseBody(response); | ||
finalResponse = { | ||
body: response.body, | ||
statusCode | ||
}; | ||
try { | ||
response.body = parseBody(response.body, options.responseType, response.statusCode); | ||
} | ||
catch (error) { | ||
if (statusCode >= 200 && statusCode < 300) { | ||
const parseError = new errors_1.ParseError(error, response, options); | ||
emitError(parseError); | ||
return; | ||
} | ||
catch (error) { | ||
if (statusCode >= 200 && statusCode < 300) { | ||
const parseError = new errors_1.ParseError(error, response, options); | ||
emitError(parseError); | ||
return; | ||
} | ||
} | ||
} | ||
const limitStatusCode = options.followRedirect ? 299 : 399; | ||
if (statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) { | ||
@@ -114,12 +128,4 @@ const error = new errors_1.HTTPError(response, options); | ||
emitter.once('error', reject); | ||
[ | ||
'request', | ||
'redirect', | ||
'uploadProgress', | ||
'downloadProgress' | ||
].forEach(event => emitter.on(event, (...args) => { | ||
proxy.emit(event, ...args); | ||
})); | ||
request_as_event_emitter_1.proxyEvents(proxy, emitter); | ||
}); | ||
promise[exports.isProxiedSymbol] = true; | ||
promise.on = (name, fn) => { | ||
@@ -129,19 +135,19 @@ proxy.on(name, fn); | ||
}; | ||
const shortcut = (responseType) => { | ||
// eslint-disable-next-line promise/prefer-await-to-then | ||
const newPromise = promise.then(() => parseBody(finalResponse.body, responseType, finalResponse.statusCode)); | ||
Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise)); | ||
// @ts-ignore The missing properties are added above | ||
return newPromise; | ||
}; | ||
promise.json = () => { | ||
options.responseType = 'json'; | ||
options.resolveBodyOnly = true; | ||
return promise; | ||
if (is_1.default.undefined(options.headers.accept)) { | ||
options.headers.accept = 'application/json'; | ||
} | ||
return shortcut('json'); | ||
}; | ||
promise.buffer = () => { | ||
options.responseType = 'buffer'; | ||
options.resolveBodyOnly = true; | ||
return promise; | ||
}; | ||
promise.text = () => { | ||
options.responseType = 'text'; | ||
options.resolveBodyOnly = true; | ||
return promise; | ||
}; | ||
promise.buffer = () => shortcut('buffer'); | ||
promise.text = () => shortcut('text'); | ||
return promise; | ||
} | ||
exports.default = asPromise; |
/// <reference types="node" /> | ||
import { Duplex as DuplexStream } from 'stream'; | ||
import { NormalizedOptions } from './utils/types'; | ||
export declare class ProxyStream extends DuplexStream { | ||
import { NormalizedOptions, GotEvents } from './utils/types'; | ||
export declare class ProxyStream extends DuplexStream implements GotEvents<ProxyStream> { | ||
isFromCache?: boolean; | ||
} | ||
export default function asStream(options: NormalizedOptions): ProxyStream; |
@@ -16,3 +16,3 @@ "use strict"; | ||
const proxy = duplexer3(input, output); | ||
const piped = new Set(); // TODO: Should be `new Set<ProxyStream>();`. | ||
const piped = new Set(); // TODO: Should be `new Set<stream.Writable>();`. | ||
let isFinished = false; | ||
@@ -26,3 +26,12 @@ options.retry.calculateDelay = () => 0; | ||
} | ||
const emitter = request_as_event_emitter_1.default(options, input); | ||
else if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') { | ||
options.body = input; | ||
} | ||
else { | ||
proxy.write = () => { | ||
proxy.destroy(); | ||
throw new TypeError(`The \`${options.method}\` method cannot be used with a body`); | ||
}; | ||
} | ||
const emitter = request_as_event_emitter_1.default(options); | ||
const emitError = async (error) => { | ||
@@ -71,4 +80,4 @@ try { | ||
// It's not possible to decompress already decompressed data, is it? | ||
const allowed = options.decompress ? key !== 'content-encoding' : true; | ||
if (allowed) { | ||
const isAllowed = options.decompress ? key !== 'content-encoding' : true; | ||
if (isAllowed) { | ||
destination.setHeader(key, value); | ||
@@ -81,14 +90,4 @@ } | ||
}); | ||
const events = [ | ||
'error', | ||
'request', | ||
'redirect', | ||
'uploadProgress', | ||
'downloadProgress' | ||
]; | ||
for (const event of events) { | ||
emitter.on(event, (...args) => { | ||
proxy.emit(event, ...args); | ||
}); | ||
} | ||
request_as_event_emitter_1.proxyEvents(proxy, emitter); | ||
emitter.on('error', (error) => proxy.emit('error', error)); | ||
const pipe = proxy.pipe.bind(proxy); | ||
@@ -95,0 +94,0 @@ const unpipe = proxy.unpipe.bind(proxy); |
@@ -6,8 +6,9 @@ "use strict"; | ||
const calculateRetryDelay = ({ attemptCount, retryOptions, error }) => { | ||
var _a; | ||
if (attemptCount > retryOptions.limit) { | ||
return 0; | ||
} | ||
const hasMethod = retryOptions.methods.has(error.options.method); | ||
const hasErrorCode = Reflect.has(error, 'code') && retryOptions.errorCodes.has(error.code); | ||
const hasStatusCode = Reflect.has(error, 'response') && retryOptions.statusCodes.has(error.response.statusCode); | ||
const hasMethod = retryOptions.methods.includes(error.options.method); | ||
const hasErrorCode = Reflect.has(error, 'code') && retryOptions.errorCodes.includes(error.code); | ||
const hasStatusCode = Reflect.has(error, 'response') && retryOptions.statusCodes.includes(error.response.statusCode); | ||
if (!hasMethod || (!hasErrorCode && !hasStatusCode)) { | ||
@@ -30,3 +31,3 @@ return 0; | ||
} | ||
if (response && response.statusCode === 413) { | ||
if (((_a = response) === null || _a === void 0 ? void 0 : _a.statusCode) === 413) { | ||
return 0; | ||
@@ -33,0 +34,0 @@ } |
@@ -0,20 +1,53 @@ | ||
/// <reference types="node" /> | ||
import { Merge } from 'type-fest'; | ||
import * as errors from './errors'; | ||
import { Options, Defaults, Response, CancelableRequest, URLOrOptions, URLArgument, ExtendedOptions } from './utils/types'; | ||
import { Options, Defaults, Response, CancelableRequest, URLOrOptions, HandlerFunction, ExtendedOptions } from './utils/types'; | ||
import { ProxyStream } from './as-stream'; | ||
import { Hooks } from './known-hook-events'; | ||
export declare type HTTPAlias = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete'; | ||
export declare type ReturnResponse = (url: URLArgument | Options & { | ||
stream?: false; | ||
url: URLArgument; | ||
export declare type ReturnStream = (url: string | Options & { | ||
isStream: true; | ||
}, options?: Options & { | ||
stream?: false; | ||
}) => CancelableRequest<Response>; | ||
export declare type ReturnStream = (url: URLArgument | Options & { | ||
stream: true; | ||
url: URLArgument; | ||
}, options?: Options & { | ||
stream: true; | ||
isStream: true; | ||
}) => ProxyStream; | ||
export declare type GotReturn = ProxyStream | CancelableRequest<Response>; | ||
export interface Got extends Record<HTTPAlias, ReturnResponse> { | ||
declare type OptionsOfDefaultResponseBody = Options & { | ||
isStream?: false; | ||
resolveBodyOnly?: false; | ||
responseType?: 'default'; | ||
}; | ||
declare type OptionsOfTextResponseBody = Options & { | ||
isStream?: false; | ||
resolveBodyOnly?: false; | ||
responseType: 'text'; | ||
}; | ||
declare type OptionsOfJSONResponseBody = Options & { | ||
isStream?: false; | ||
resolveBodyOnly?: false; | ||
responseType: 'json'; | ||
}; | ||
declare type OptionsOfBufferResponseBody = Options & { | ||
isStream?: false; | ||
resolveBodyOnly?: false; | ||
responseType: 'buffer'; | ||
}; | ||
declare type ResponseBodyOnly = { | ||
resolveBodyOnly: true; | ||
}; | ||
interface GotFunctions { | ||
(url: string | OptionsOfDefaultResponseBody, options?: OptionsOfDefaultResponseBody): CancelableRequest<Response>; | ||
(url: string | OptionsOfTextResponseBody, options?: OptionsOfTextResponseBody): CancelableRequest<Response<string>>; | ||
(url: string | OptionsOfJSONResponseBody, options?: OptionsOfJSONResponseBody): CancelableRequest<Response<object>>; | ||
(url: string | OptionsOfBufferResponseBody, options?: OptionsOfBufferResponseBody): CancelableRequest<Response<Buffer>>; | ||
(url: string | OptionsOfDefaultResponseBody & ResponseBodyOnly, options?: OptionsOfDefaultResponseBody & ResponseBodyOnly): CancelableRequest<any>; | ||
(url: string | OptionsOfTextResponseBody & ResponseBodyOnly, options?: OptionsOfTextResponseBody & ResponseBodyOnly): CancelableRequest<string>; | ||
(url: string | OptionsOfJSONResponseBody & ResponseBodyOnly, options?: OptionsOfJSONResponseBody & ResponseBodyOnly): CancelableRequest<object>; | ||
(url: string | OptionsOfBufferResponseBody & ResponseBodyOnly, options?: OptionsOfBufferResponseBody & ResponseBodyOnly): CancelableRequest<Buffer>; | ||
(url: string | Options & { | ||
isStream: true; | ||
}, options?: Options & { | ||
isStream: true; | ||
}): ProxyStream; | ||
} | ||
export interface Got extends Merge<Record<HTTPAlias, GotFunctions>, GotFunctions> { | ||
stream: GotStream; | ||
@@ -32,16 +65,2 @@ defaults: Defaults | Readonly<Defaults>; | ||
CancelError: typeof errors.CancelError; | ||
(url: URLArgument | Options & { | ||
stream?: false; | ||
url: URLArgument; | ||
}, options?: Options & { | ||
stream?: false; | ||
}): CancelableRequest<Response>; | ||
(url: URLArgument | Options & { | ||
stream: true; | ||
url: URLArgument; | ||
}, options?: Options & { | ||
stream: true; | ||
}): ProxyStream; | ||
(url: URLOrOptions, options?: Options): CancelableRequest<Response> | ProxyStream; | ||
create(defaults: Defaults): Got; | ||
extend(...instancesOrOptions: Array<Got | ExtendedOptions>): Got; | ||
@@ -56,3 +75,4 @@ mergeInstances(parent: Got, ...instances: Got[]): Got; | ||
} | ||
declare const create: (defaults: Partial<Defaults>) => Got; | ||
export declare const defaultHandler: HandlerFunction; | ||
declare const create: (defaults: Defaults) => Got; | ||
export default create; |
@@ -5,7 +5,6 @@ "use strict"; | ||
const deep_freeze_1 = require("./utils/deep-freeze"); | ||
const merge_1 = require("./merge"); | ||
const as_promise_1 = require("./as-promise"); | ||
const as_stream_1 = require("./as-stream"); | ||
const normalize_arguments_1 = require("./normalize-arguments"); | ||
const getPromiseOrStream = (options) => options.stream ? as_stream_1.default(options) : as_promise_1.default(options); | ||
const getPromiseOrStream = (options) => options.isStream ? as_stream_1.default(options) : as_promise_1.default(options); | ||
const aliases = [ | ||
@@ -19,51 +18,33 @@ 'get', | ||
]; | ||
const defaultHandler = (options, next) => next(options); | ||
// `got.mergeInstances()` is deprecated | ||
let hasShownDeprecation = false; | ||
exports.defaultHandler = (options, next) => next(options); | ||
const create = (defaults) => { | ||
defaults = merge_1.default({}, defaults); | ||
normalize_arguments_1.preNormalizeArguments(defaults.options); | ||
defaults = { | ||
handlers: [defaultHandler], | ||
options: {}, | ||
...defaults, | ||
mutableDefaults: Boolean(defaults.mutableDefaults) | ||
}; | ||
// Proxy properties from next handlers | ||
defaults._rawHandlers = defaults.handlers; | ||
defaults.handlers = defaults.handlers.map(fn => ((options, next) => { | ||
let root; | ||
const result = fn(options, newOptions => { | ||
root = next(newOptions); | ||
return root; | ||
}); | ||
if (result !== root && !options.isStream) { | ||
Object.setPrototypeOf(result, Object.getPrototypeOf(root)); | ||
Object.defineProperties(result, Object.getOwnPropertyDescriptors(root)); | ||
} | ||
return result; | ||
})); | ||
// @ts-ignore Because the for loop handles it for us, as well as the other Object.defines | ||
const got = (url, options) => { | ||
const isStream = options && options.stream; | ||
var _a; | ||
let iteration = 0; | ||
const iterateHandlers = (newOptions) => { | ||
let nextPromise; | ||
const result = defaults.handlers[iteration++](newOptions, options => { | ||
const fn = iteration === defaults.handlers.length ? getPromiseOrStream : iterateHandlers; | ||
if (isStream) { | ||
return fn(options); | ||
} | ||
// We need to remember the `next(options)` result. | ||
nextPromise = fn(options); | ||
return nextPromise; | ||
}); | ||
// Proxy the properties from the next handler to this one | ||
if (!isStream && !Reflect.has(result, as_promise_1.isProxiedSymbol)) { | ||
for (const key of Object.keys(nextPromise)) { | ||
Object.defineProperty(result, key, { | ||
get: () => { | ||
return nextPromise[key]; | ||
}, | ||
set: (value) => { | ||
nextPromise[key] = value; | ||
} | ||
}); | ||
} | ||
result.cancel = nextPromise.cancel; | ||
result[as_promise_1.isProxiedSymbol] = true; | ||
} | ||
return result; | ||
const iterateHandlers = newOptions => { | ||
return defaults.handlers[iteration++](newOptions, | ||
// @ts-ignore TS doesn't know that it calls `getPromiseOrStream` at the end | ||
iteration === defaults.handlers.length ? getPromiseOrStream : iterateHandlers); | ||
}; | ||
try { | ||
// @ts-ignore This handler takes only one parameter. | ||
return iterateHandlers(normalize_arguments_1.normalizeArguments(url, options, defaults)); | ||
} | ||
catch (error) { | ||
if (isStream) { | ||
if ((_a = options) === null || _a === void 0 ? void 0 : _a.isStream) { | ||
throw error; | ||
@@ -77,15 +58,14 @@ } | ||
}; | ||
got.create = create; | ||
got.extend = (...instancesOrOptions) => { | ||
const options = [defaults.options]; | ||
const handlers = [...defaults.handlers]; | ||
const optionsArray = [defaults.options]; | ||
let handlers = [...defaults._rawHandlers]; | ||
let mutableDefaults; | ||
for (const value of instancesOrOptions) { | ||
if (Reflect.has(value, 'defaults')) { | ||
options.push(value.defaults.options); | ||
handlers.push(...value.defaults.handlers.filter(handler => handler !== defaultHandler)); | ||
optionsArray.push(value.defaults.options); | ||
handlers.push(...value.defaults._rawHandlers); | ||
mutableDefaults = value.defaults.mutableDefaults; | ||
} | ||
else { | ||
options.push(value); | ||
optionsArray.push(value); | ||
if (Reflect.has(value, 'handlers')) { | ||
@@ -97,5 +77,8 @@ handlers.push(...value.handlers); | ||
} | ||
handlers.push(defaultHandler); | ||
handlers = handlers.filter(handler => handler !== exports.defaultHandler); | ||
if (handlers.length === 0) { | ||
handlers.push(exports.defaultHandler); | ||
} | ||
return create({ | ||
options: merge_1.mergeOptions(...options), | ||
options: normalize_arguments_1.mergeOptions(...optionsArray), | ||
handlers, | ||
@@ -105,16 +88,10 @@ mutableDefaults | ||
}; | ||
got.mergeInstances = (parent, ...instances) => { | ||
if (!hasShownDeprecation) { | ||
console.warn('`got.mergeInstances()` is deprecated. We support it solely for compatibility - it will be removed in Got 11. Use `instance.extend(...instances)` instead.'); | ||
hasShownDeprecation = true; | ||
} | ||
return parent.extend(...instances); | ||
}; | ||
// @ts-ignore The missing methods because the for-loop handles it for us | ||
got.stream = (url, options) => got(url, { ...options, stream: true }); | ||
got.stream = (url, options) => got(url, { ...options, isStream: true }); | ||
for (const method of aliases) { | ||
// @ts-ignore | ||
got[method] = (url, options) => got(url, { ...options, method }); | ||
got.stream[method] = (url, options) => got.stream(url, { ...options, method }); | ||
} | ||
Object.assign(got, { ...errors, mergeOptions: merge_1.mergeOptions }); | ||
Object.assign(got, { ...errors, mergeOptions: normalize_arguments_1.mergeOptions }); | ||
Object.defineProperty(got, 'defaults', { | ||
@@ -121,0 +98,0 @@ value: defaults.mutableDefaults ? defaults : deep_freeze_1.default(defaults), |
@@ -32,3 +32,3 @@ import { Timings } from '@szmarczak/http-timer'; | ||
readonly response: Response; | ||
constructor(response: Response, options: NormalizedOptions); | ||
constructor(response: Response, maxRedirects: number, options: NormalizedOptions); | ||
} | ||
@@ -35,0 +35,0 @@ export declare class UnsupportedProtocolError extends GotError { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const url_1 = require("url"); | ||
const is_1 = require("@sindresorhus/is"); | ||
@@ -42,3 +41,3 @@ class GotError extends Error { | ||
constructor(error, response, options) { | ||
super(`${error.message} in "${url_1.format(options)}"`, error, options); | ||
super(`${error.message} in "${options.url.toString()}"`, error, options); | ||
this.name = 'ParseError'; | ||
@@ -63,4 +62,4 @@ Object.defineProperty(this, 'response', { | ||
class MaxRedirectsError extends GotError { | ||
constructor(response, options) { | ||
super('Redirected 10 times. Aborting.', {}, options); | ||
constructor(response, maxRedirects, options) { | ||
super(`Redirected ${maxRedirects} times. Aborting.`, {}, options); | ||
this.name = 'MaxRedirectsError'; | ||
@@ -75,3 +74,3 @@ Object.defineProperty(this, 'response', { | ||
constructor(options) { | ||
super(`Unsupported protocol "${options.protocol}"`, {}, options); | ||
super(`Unsupported protocol "${options.url.protocol}"`, {}, options); | ||
this.name = 'UnsupportedProtocolError'; | ||
@@ -78,0 +77,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const stream = require("stream"); | ||
const is_1 = require("@sindresorhus/is"); | ||
const decompressResponse = require("decompress-response"); | ||
@@ -9,9 +8,9 @@ const mimicResponse = require("mimic-response"); | ||
exports.default = (response, options, emitter) => { | ||
var _a; | ||
const downloadBodySize = Number(response.headers['content-length']) || undefined; | ||
const progressStream = progress_1.downloadProgress(response, emitter, downloadBodySize); | ||
const progressStream = progress_1.downloadProgress(emitter, downloadBodySize); | ||
mimicResponse(response, progressStream); | ||
const newResponse = (options.decompress === true && | ||
is_1.default.function_(decompressResponse) && | ||
const newResponse = (options.decompress && | ||
options.method !== 'HEAD' ? decompressResponse(progressStream) : progressStream); | ||
if (!options.decompress && ['gzip', 'deflate', 'br'].includes(response.headers['content-encoding'] || '')) { | ||
if (!options.decompress && ['gzip', 'deflate', 'br'].includes((_a = response.headers['content-encoding'], (_a !== null && _a !== void 0 ? _a : '')))) { | ||
options.encoding = null; | ||
@@ -18,0 +17,0 @@ } |
declare const got: import("./create").Got; | ||
export default got; | ||
export * from './utils/types'; | ||
export { Got, GotStream, ReturnResponse, ReturnStream, GotReturn } from './create'; | ||
export { Got, GotStream, ReturnStream, GotReturn } from './create'; | ||
export { ProxyStream as ResponseStream } from './as-stream'; | ||
export { GotError, CacheError, RequestError, ParseError, HTTPError, MaxRedirectsError, UnsupportedProtocolError, TimeoutError, CancelError } from './errors'; | ||
export { InitHook, BeforeRequestHook, BeforeRedirectHook, BeforeRetryHook, BeforeErrorHook, AfterResponseHook, HookType, Hooks, HookEvent } from './known-hook-events'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
const create_1 = require("./create"); | ||
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf8')); | ||
const defaults = { | ||
@@ -38,6 +35,9 @@ options: { | ||
'EAI_AGAIN' | ||
] | ||
], | ||
maxRetryAfter: undefined, | ||
calculateDelay: ({ computedValue }) => computedValue | ||
}, | ||
timeout: {}, | ||
headers: { | ||
'user-agent': `${packageJson.name}/${packageJson.version} (https://github.com/sindresorhus/got)` | ||
'user-agent': 'got (https://github.com/sindresorhus/got)' | ||
}, | ||
@@ -53,9 +53,12 @@ hooks: { | ||
followRedirect: true, | ||
stream: false, | ||
isStream: false, | ||
cache: false, | ||
dnsCache: false, | ||
useElectronNet: false, | ||
responseType: 'text', | ||
resolveBodyOnly: false | ||
responseType: 'default', | ||
resolveBodyOnly: false, | ||
maxRedirects: 10, | ||
prefixUrl: '' | ||
}, | ||
handlers: [create_1.defaultHandler], | ||
mutableDefaults: false | ||
@@ -62,0 +65,0 @@ }; |
@@ -1,3 +0,2 @@ | ||
import { Options, CancelableRequest, Response, NormalizedOptions } from './utils/types'; | ||
import { HTTPError, GotError, ParseError, MaxRedirectsError } from './errors'; | ||
import { CancelableRequest, Response, NormalizedOptions, GenericError } from './utils/types'; | ||
/** | ||
@@ -10,3 +9,3 @@ Called with plain request options, right before their normalization. This is especially useful in conjunction with got.extend() and got.create() when the input needs custom handling. | ||
*/ | ||
export declare type InitHook = (options: Options) => void; | ||
export declare type InitHook = (options: NormalizedOptions) => void; | ||
/** | ||
@@ -25,3 +24,3 @@ Called with normalized [request options](https://github.com/sindresorhus/got#options). Got will make no further changes to the request before it is sent (except the body serialization). This is especially useful in conjunction with [`got.extend()`](https://github.com/sindresorhus/got#instances) and [`got.create()`](https://github.com/sindresorhus/got/blob/master/advanced-creation.md) when you want to create an API client that, for example, uses HMAC-signing. | ||
*/ | ||
export declare type BeforeRetryHook = (options: NormalizedOptions, error: Error | GotError | ParseError | HTTPError | MaxRedirectsError, retryCount: number) => void | Promise<void>; | ||
export declare type BeforeRetryHook = (options: NormalizedOptions, error?: GenericError, retryCount?: number) => void | Promise<void>; | ||
/** | ||
@@ -32,3 +31,3 @@ 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. | ||
*/ | ||
export declare type BeforeErrorHook = <ErrorLike extends Error | GotError | ParseError | HTTPError | MaxRedirectsError>(error: ErrorLike) => Error | Promise<Error>; | ||
export declare type BeforeErrorHook = <ErrorLike extends GenericError>(error: ErrorLike) => Error | Promise<Error>; | ||
/** | ||
@@ -53,3 +52,3 @@ Called with [response object](https://github.com/sindresorhus/got#response) and a retry function. | ||
*/ | ||
init: InitHook[]; | ||
init?: InitHook[]; | ||
/** | ||
@@ -61,3 +60,3 @@ Called with normalized [request options](https://github.com/sindresorhus/got#options). Got will make no further changes to the request before it is sent (except the body serialization). This is especially useful in conjunction with [`got.extend()`](https://github.com/sindresorhus/got#instances) and [`got.create()`](https://github.com/sindresorhus/got/blob/master/advanced-creation.md) when you want to create an API client that, for example, uses HMAC-signing. | ||
*/ | ||
beforeRequest: BeforeRequestHook[]; | ||
beforeRequest?: BeforeRequestHook[]; | ||
/** | ||
@@ -68,3 +67,3 @@ Called with normalized [request options](https://github.com/sindresorhus/got#options). Got will make no further changes to the request. This is especially useful when you want to avoid dead sites. | ||
*/ | ||
beforeRedirect: BeforeRedirectHook[]; | ||
beforeRedirect?: BeforeRedirectHook[]; | ||
/** | ||
@@ -75,3 +74,3 @@ Called with normalized [request options](https://github.com/sindresorhus/got#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. | ||
*/ | ||
beforeRetry: BeforeRetryHook[]; | ||
beforeRetry?: BeforeRetryHook[]; | ||
/** | ||
@@ -84,3 +83,3 @@ 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. | ||
*/ | ||
beforeError: BeforeErrorHook[]; | ||
beforeError?: BeforeErrorHook[]; | ||
/** | ||
@@ -93,3 +92,3 @@ Called with [response object](https://github.com/sindresorhus/got#response) and a retry function. | ||
*/ | ||
afterResponse: AfterResponseHook[]; | ||
afterResponse?: AfterResponseHook[]; | ||
} | ||
@@ -96,0 +95,0 @@ export declare type HookEvent = keyof Hooks; |
@@ -1,4 +0,11 @@ | ||
import { Options, Defaults, NormalizedOptions, URLOrOptions } from './utils/types'; | ||
export declare const preNormalizeArguments: (options: Options, defaults?: Options) => NormalizedOptions; | ||
export declare const normalizeArguments: (url: URLOrOptions, options: NormalizedOptions, defaults?: Defaults) => NormalizedOptions; | ||
export declare const reNormalizeArguments: (options: NormalizedOptions) => NormalizedOptions; | ||
/// <reference types="node" /> | ||
import https = require('https'); | ||
import { Options, NormalizedOptions, URLOrOptions, Defaults } from './utils/types'; | ||
export declare const preNormalizeArguments: (options: Options, defaults?: NormalizedOptions) => NormalizedOptions; | ||
export declare const mergeOptions: (...sources: Options[]) => NormalizedOptions; | ||
export declare const normalizeArguments: (url: URLOrOptions, options?: Options, defaults?: Defaults) => NormalizedOptions; | ||
export declare type NormalizedRequestArguments = https.RequestOptions & { | ||
body: Pick<NormalizedOptions, 'body'>; | ||
url: Pick<NormalizedOptions, 'url'>; | ||
}; | ||
export declare const normalizeRequestArguments: (options: NormalizedOptions) => Promise<NormalizedRequestArguments>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const url_1 = require("url"); | ||
const util_1 = require("util"); | ||
const http = require("http"); | ||
const https = require("https"); | ||
const cacheable_lookup_1 = require("cacheable-lookup"); | ||
const CacheableRequest = require("cacheable-request"); | ||
const is_1 = require("@sindresorhus/is"); | ||
const lowercaseKeys = require("lowercase-keys"); | ||
const url_to_options_1 = require("./utils/url-to-options"); | ||
const validate_search_params_1 = require("./utils/validate-search-params"); | ||
const toReadableStream = require("to-readable-stream"); | ||
const options_to_url_1 = require("./utils/options-to-url"); | ||
const errors_1 = require("./errors"); | ||
const merge_1 = require("./utils/merge"); | ||
const known_hook_events_1 = require("./known-hook-events"); | ||
const dynamic_require_1 = require("./utils/dynamic-require"); | ||
const get_body_size_1 = require("./utils/get-body-size"); | ||
const is_form_data_1 = require("./utils/is-form-data"); | ||
const supports_brotli_1 = require("./utils/supports-brotli"); | ||
const merge_1 = require("./merge"); | ||
const known_hook_events_1 = require("./known-hook-events"); | ||
let hasShownDeprecation = false; | ||
// `preNormalize` handles static options (e.g. headers). | ||
// For example, when you create a custom instance and make a request | ||
// with no static changes, they won't be normalized again. | ||
// | ||
// `normalize` operates on dynamic options - they cannot be saved. | ||
// For example, `body` is every time different per request. | ||
// When it's done normalizing the new options, it performs merge() | ||
// on the prenormalized options and the normalized ones. | ||
const nonEnumerableProperties = [ | ||
'context', | ||
'body', | ||
'json', | ||
'form' | ||
]; | ||
exports.preNormalizeArguments = (options, defaults) => { | ||
if (is_1.default.nullOrUndefined(options.headers)) { | ||
var _a; | ||
// `options.headers` | ||
if (is_1.default.undefined(options.headers)) { | ||
options.headers = {}; | ||
@@ -28,20 +34,21 @@ } | ||
} | ||
if (options.prefixUrl) { | ||
// `options.prefixUrl` | ||
if (is_1.default.urlInstance(options.prefixUrl) || is_1.default.string(options.prefixUrl)) { | ||
options.prefixUrl = options.prefixUrl.toString(); | ||
if (!options.prefixUrl.toString().endsWith('/')) { | ||
if (options.prefixUrl.length !== 0 && !options.prefixUrl.endsWith('/')) { | ||
options.prefixUrl += '/'; | ||
} | ||
} | ||
if (is_1.default.nullOrUndefined(options.hooks)) { | ||
else { | ||
options.prefixUrl = defaults ? defaults.prefixUrl : ''; | ||
} | ||
// `options.hooks` | ||
if (is_1.default.undefined(options.hooks)) { | ||
options.hooks = {}; | ||
} | ||
else if (!is_1.default.object(options.hooks)) { | ||
throw new TypeError(`Parameter \`hooks\` must be an object, not ${is_1.default(options.hooks)}`); | ||
} | ||
for (const event of known_hook_events_1.default) { | ||
if (is_1.default.nullOrUndefined(options.hooks[event])) { | ||
if (defaults && defaults.hooks) { | ||
if (event in defaults.hooks) { | ||
// @ts-ignore | ||
options.hooks[event] = defaults.hooks[event].slice(); | ||
if (is_1.default.object(options.hooks)) { | ||
for (const event of known_hook_events_1.default) { | ||
if (Reflect.has(options.hooks, event)) { | ||
if (!is_1.default.array(options.hooks[event])) { | ||
throw new TypeError(`Parameter \`${event}\` must be an Array, not ${is_1.default(options.hooks[event])}`); | ||
} | ||
@@ -54,130 +61,262 @@ } | ||
} | ||
else { | ||
throw new TypeError(`Parameter \`hooks\` must be an Object, not ${is_1.default(options.hooks)}`); | ||
} | ||
if (defaults) { | ||
for (const event of known_hook_events_1.default) { | ||
// @ts-ignore TS is dumb. | ||
options.hooks[event] = [ | ||
...defaults.hooks[event], | ||
...options.hooks[event] | ||
]; | ||
} | ||
} | ||
// `options.timeout` | ||
if (is_1.default.number(options.timeout)) { | ||
options.gotTimeout = { request: options.timeout }; | ||
options.timeout = { request: options.timeout }; | ||
} | ||
else if (is_1.default.object(options.timeout)) { | ||
options.gotTimeout = options.timeout; | ||
else if (!is_1.default.object(options.timeout)) { | ||
options.timeout = {}; | ||
} | ||
delete options.timeout; | ||
// `options.retry` | ||
const { retry } = options; | ||
options.retry = { | ||
calculateDelay: retryObject => retryObject.computedValue, | ||
methods: new Set(), | ||
statusCodes: new Set(), | ||
errorCodes: new Set(), | ||
maxRetryAfter: undefined | ||
}; | ||
if (is_1.default.nonEmptyObject(defaults) && retry !== false) { | ||
if (defaults) { | ||
options.retry = { ...defaults.retry }; | ||
} | ||
if (retry !== false) { | ||
if (is_1.default.number(retry)) { | ||
options.retry.limit = retry; | ||
} | ||
else { | ||
// @ts-ignore | ||
const retryOption = { ...options.retry, ...retry }; | ||
options.retry = retryOption; | ||
} | ||
else { | ||
options.retry = { | ||
calculateDelay: retryObject => retryObject.computedValue, | ||
limit: 0, | ||
methods: [], | ||
statusCodes: [], | ||
errorCodes: [], | ||
maxRetryAfter: undefined | ||
}; | ||
} | ||
if (!options.retry.maxRetryAfter && options.gotTimeout) { | ||
options.retry.maxRetryAfter = Math.min(...[options.gotTimeout.request, options.gotTimeout.connect].filter(n => !is_1.default.nullOrUndefined(n))); | ||
if (is_1.default.object(retry)) { | ||
options.retry = { | ||
...options.retry, | ||
...retry | ||
}; | ||
} | ||
if (is_1.default.array(options.retry.methods)) { | ||
options.retry.methods = new Set(options.retry.methods.map(method => method.toUpperCase())); | ||
else if (is_1.default.number(retry)) { | ||
options.retry.limit = retry; | ||
} | ||
if (is_1.default.array(options.retry.statusCodes)) { | ||
options.retry.statusCodes = new Set(options.retry.statusCodes); | ||
if (options.retry.maxRetryAfter === undefined) { | ||
options.retry.maxRetryAfter = Math.min(...[options.timeout.request, options.timeout.connect].filter(n => !is_1.default.nullOrUndefined(n))); | ||
} | ||
if (is_1.default.array(options.retry.errorCodes)) { | ||
options.retry.errorCodes = new Set(options.retry.errorCodes); | ||
options.retry.methods = [...new Set(options.retry.methods.map(method => method.toUpperCase()))]; | ||
options.retry.statusCodes = [...new Set(options.retry.statusCodes)]; | ||
options.retry.errorCodes = [...new Set(options.retry.errorCodes)]; | ||
// `options.dnsCache` | ||
if (options.dnsCache && !(options.dnsCache instanceof cacheable_lookup_1.default)) { | ||
options.dnsCache = new cacheable_lookup_1.default({ cacheAdapter: options.dnsCache }); | ||
} | ||
if (options.dnsCache) { | ||
const cacheableLookup = new cacheable_lookup_1.default({ cacheAdapter: options.dnsCache }); | ||
options.lookup = cacheableLookup.lookup; | ||
delete options.dnsCache; | ||
// `options.method` | ||
if (is_1.default.string(options.method)) { | ||
options.method = options.method.toUpperCase(); | ||
} | ||
return options; | ||
}; | ||
exports.normalizeArguments = (url, options, defaults) => { | ||
let urlArgument; | ||
if (is_1.default.plainObject(url)) { | ||
options = { ...url, ...options }; | ||
urlArgument = options.url || {}; | ||
delete options.url; | ||
} | ||
else { | ||
urlArgument = url; | ||
options.method = ((_a = defaults) === null || _a === void 0 ? void 0 : _a.method) || 'GET'; | ||
} | ||
if (defaults) { | ||
options = merge_1.default({}, defaults.options, options ? exports.preNormalizeArguments(options, defaults.options) : {}); | ||
// Better memory management, so we don't have to generate a new object every time | ||
if (options.cache) { | ||
options.cacheableRequest = new CacheableRequest((options, handler) => options.request(options, handler), options.cache); | ||
} | ||
else { | ||
options = merge_1.default({}, exports.preNormalizeArguments(options)); | ||
// `options.cookieJar` | ||
if (is_1.default.object(options.cookieJar)) { | ||
let { setCookie, getCookieString } = options.cookieJar; | ||
// Horrible `tough-cookie` check | ||
if (setCookie.length === 4 && getCookieString.length === 0) { | ||
if (!Reflect.has(setCookie, util_1.promisify.custom)) { | ||
setCookie = util_1.promisify(setCookie.bind(options.cookieJar)); | ||
getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar)); | ||
} | ||
} | ||
else if (setCookie.length !== 2) { | ||
throw new TypeError('`options.cookieJar.setCookie` needs to be an async function with 2 arguments'); | ||
} | ||
else if (getCookieString.length !== 1) { | ||
throw new TypeError('`options.cookieJar.getCookieString` needs to be an async function with 1 argument'); | ||
} | ||
options.cookieJar = { setCookie, getCookieString }; | ||
} | ||
if (!is_1.default.string(urlArgument) && !is_1.default.object(urlArgument)) { | ||
throw new TypeError(`Parameter \`url\` must be a string or object, not ${is_1.default(urlArgument)}`); | ||
} | ||
let urlObj; | ||
if (is_1.default.string(urlArgument)) { | ||
if (options.prefixUrl && urlArgument.startsWith('/')) { | ||
throw new Error('`url` must not begin with a slash when using `prefixUrl`'); | ||
return options; | ||
}; | ||
exports.mergeOptions = (...sources) => { | ||
const mergedOptions = exports.preNormalizeArguments({}); | ||
// Non enumerable properties shall not be merged | ||
const properties = {}; | ||
for (const source of sources) { | ||
if (!source) { | ||
continue; | ||
} | ||
if (options.prefixUrl) { | ||
urlArgument = options.prefixUrl + urlArgument; | ||
merge_1.default(mergedOptions, exports.preNormalizeArguments(merge_1.default({}, source), mergedOptions)); | ||
for (const name of nonEnumerableProperties) { | ||
if (!Reflect.has(source, name)) { | ||
continue; | ||
} | ||
properties[name] = { | ||
writable: true, | ||
configurable: true, | ||
enumerable: false, | ||
value: source[name] | ||
}; | ||
} | ||
urlArgument = urlArgument.replace(/^unix:/, 'http://$&'); | ||
urlObj = url_to_options_1.default(new URL(urlArgument)); | ||
} | ||
else if (is_1.default.urlInstance(urlArgument)) { | ||
urlObj = url_to_options_1.default(urlArgument); | ||
Object.defineProperties(mergedOptions, properties); | ||
return mergedOptions; | ||
}; | ||
exports.normalizeArguments = (url, options, defaults) => { | ||
// Merge options | ||
if (typeof url === 'undefined') { | ||
throw new TypeError('Missing `url` argument'); | ||
} | ||
else if (options.prefixUrl) { | ||
urlObj = { | ||
...url_to_options_1.default(new URL(options.prefixUrl)), | ||
...urlArgument | ||
}; | ||
if (typeof options === 'undefined') { | ||
options = {}; | ||
} | ||
if (is_1.default.urlInstance(url) || is_1.default.string(url)) { | ||
options.url = url; | ||
options = exports.mergeOptions(defaults && defaults.options, options); | ||
} | ||
else { | ||
urlObj = urlArgument; | ||
if (Reflect.has(url, 'resolve')) { | ||
throw new Error('The legacy `url.Url` is deprecated. Use `URL` instead.'); | ||
} | ||
options = exports.mergeOptions(defaults && defaults.options, url, options); | ||
} | ||
// Override both null/undefined with default protocol | ||
options = merge_1.default({ path: '' }, urlObj, { protocol: urlObj.protocol || 'https:' }, options); | ||
for (const hook of options.hooks.init) { | ||
const isCalled = hook(options); | ||
if (is_1.default.promise(isCalled)) { | ||
throw new TypeError('The `init` hook must be a synchronous function'); | ||
// Normalize URL | ||
// TODO: drop `optionsToUrl` in Got 12 | ||
if (is_1.default.string(options.url)) { | ||
options.url = options.prefixUrl + options.url; | ||
options.url = options.url.replace(/^unix:/, 'http://$&'); | ||
if (options.searchParams || options.search) { | ||
options.url = options.url.split('?')[0]; | ||
} | ||
options.url = options_to_url_1.default({ | ||
origin: options.url, | ||
...options | ||
}); | ||
} | ||
const { prefixUrl } = options; | ||
else if (!is_1.default.urlInstance(options.url)) { | ||
options.url = options_to_url_1.default({ origin: options.prefixUrl, ...options }); | ||
} | ||
const normalizedOptions = options; | ||
// Make it possible to change `options.prefixUrl` | ||
let prefixUrl = options.prefixUrl; | ||
Object.defineProperty(options, 'prefixUrl', { | ||
set: () => { | ||
throw new Error('Failed to set prefixUrl. Options are normalized already.'); | ||
set: (value) => { | ||
if (!normalizedOptions.url.href.startsWith(value)) { | ||
throw new Error(`Cannot change \`prefixUrl\` from ${prefixUrl} to ${value}: ${normalizedOptions.url.href}`); | ||
} | ||
normalizedOptions.url = new URL(value + normalizedOptions.url.href.slice(prefixUrl.length)); | ||
prefixUrl = value; | ||
}, | ||
get: () => prefixUrl | ||
}); | ||
let { searchParams } = options; | ||
delete options.searchParams; | ||
// TODO: Remove this before Got v11 | ||
if (options.query) { | ||
if (!hasShownDeprecation) { | ||
console.warn('`options.query` is deprecated. We support it solely for compatibility - it will be removed in Got 11. Use `options.searchParams` instead.'); | ||
hasShownDeprecation = true; | ||
// Make it possible to remove default headers | ||
for (const [key, value] of Object.entries(options.headers)) { | ||
if (is_1.default.undefined(value)) { | ||
delete options.headers[key]; | ||
} | ||
searchParams = options.query; | ||
delete options.query; | ||
else if (is_1.default.null_(value)) { | ||
throw new TypeError('Use `undefined` instead of `null` to delete HTTP headers'); | ||
} | ||
} | ||
if (is_1.default.nonEmptyString(searchParams) || is_1.default.nonEmptyObject(searchParams) || (searchParams && searchParams instanceof URLSearchParams)) { | ||
if (!is_1.default.string(searchParams)) { | ||
if (!(searchParams instanceof URLSearchParams)) { | ||
validate_search_params_1.default(searchParams); | ||
for (const hook of options.hooks.init) { | ||
if (is_1.default.asyncFunction(hook)) { | ||
throw new TypeError('The `init` hook must be a synchronous function'); | ||
} | ||
// @ts-ignore TS is dumb. | ||
hook(normalizedOptions); | ||
} | ||
return normalizedOptions; | ||
}; | ||
const withoutBody = new Set(['GET', 'HEAD']); | ||
exports.normalizeRequestArguments = async (options) => { | ||
var _a, _b; | ||
options = exports.mergeOptions(options); | ||
let uploadBodySize; | ||
// Serialize body | ||
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); | ||
if ((isBody || isForm || isJSON) && withoutBody.has(options.method)) { | ||
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) { | ||
if (is_1.default.object(options.body) && is_form_data_1.default(options.body)) { | ||
// Special case for https://github.com/form-data/form-data | ||
if (!Reflect.has(headers, 'content-type')) { | ||
headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`; | ||
} | ||
searchParams = (new URLSearchParams(searchParams)).toString(); | ||
} | ||
options.path = `${options.path.split('?')[0]}?${searchParams}`; | ||
else if (!is_1.default.nodeStream(options.body) && !is_1.default.string(options.body) && !is_1.default.buffer(options.body)) { | ||
throw new TypeError('The `body` option must be a stream.Readable, string or Buffer'); | ||
} | ||
} | ||
if (options.hostname === 'unix') { | ||
const matches = /(?<socketPath>.+?):(?<path>.+)/.exec(options.path); | ||
if (matches) { | ||
else if (isForm) { | ||
if (!is_1.default.object(options.form)) { | ||
throw new TypeError('The `form` option must be an Object'); | ||
} | ||
if (!Reflect.has(headers, 'content-type')) { | ||
headers['content-type'] = 'application/x-www-form-urlencoded'; | ||
} | ||
options.body = (new URLSearchParams(options.form)).toString(); | ||
} | ||
else if (isJSON) { | ||
if (!Reflect.has(headers, 'content-type')) { | ||
headers['content-type'] = 'application/json'; | ||
} | ||
options.body = JSON.stringify(options.json); | ||
} | ||
// Convert buffer to stream to receive upload progress events (#322) | ||
if (is_1.default.buffer(options.body)) { | ||
uploadBodySize = options.body.length; | ||
options.body = toReadableStream(options.body); | ||
} | ||
else { | ||
uploadBodySize = await get_body_size_1.default(options); | ||
} | ||
// 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 (!Reflect.has(headers, 'content-length') && !Reflect.has(headers, 'transfer-encoding')) { | ||
if ((options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') && | ||
!is_1.default.undefined(uploadBodySize)) { | ||
headers['content-length'] = String(uploadBodySize); | ||
} | ||
} | ||
if (!options.isStream && options.responseType === 'json' && is_1.default.undefined(headers.accept)) { | ||
headers.accept = 'application/json'; | ||
} | ||
if (options.decompress && is_1.default.undefined(headers['accept-encoding'])) { | ||
headers['accept-encoding'] = supports_brotli_1.default ? 'gzip, deflate, br' : 'gzip, deflate'; | ||
} | ||
// Validate URL | ||
if (options.url.protocol !== 'http:' && options.url.protocol !== 'https:') { | ||
throw new errors_1.UnsupportedProtocolError(options); | ||
} | ||
decodeURI(options.url.toString()); | ||
// Normalize request function | ||
if (!is_1.default.function_(options.request)) { | ||
options.request = options.url.protocol === 'https:' ? https.request : http.request; | ||
} | ||
// UNIX sockets | ||
if (options.url.hostname === 'unix') { | ||
const matches = /(?<socketPath>.+?):(?<path>.+)/.exec(options.url.pathname); | ||
if ((_a = matches) === null || _a === void 0 ? void 0 : _a.groups) { | ||
const { socketPath, path } = matches.groups; | ||
// It's a bug! | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
options = { | ||
@@ -191,16 +330,30 @@ ...options, | ||
} | ||
const { headers } = options; | ||
for (const [key, value] of Object.entries(headers)) { | ||
if (is_1.default.nullOrUndefined(value)) { | ||
delete headers[key]; | ||
} | ||
if (is_1.default.object(options.agent)) { | ||
options.agent = options.agent[options.url.protocol.slice(0, -1)] || options.agent; | ||
} | ||
if (options.decompress && is_1.default.undefined(headers['accept-encoding'])) { | ||
headers['accept-encoding'] = supports_brotli_1.default ? 'gzip, deflate, br' : 'gzip, deflate'; | ||
if (options.dnsCache) { | ||
options.lookup = options.dnsCache.lookup; | ||
} | ||
if (options.method) { | ||
options.method = options.method.toUpperCase(); | ||
/* istanbul ignore next: electron.net is broken */ | ||
// No point in typing process.versions correctly, as | ||
// `process.version.electron` is used only once, right here. | ||
if (options.useElectronNet && process.versions.electron) { | ||
const electron = dynamic_require_1.default(module, 'electron'); // Trick webpack | ||
options.request = (_b = electron.net.request, (_b !== null && _b !== void 0 ? _b : electron.remote.net.request)); | ||
} | ||
// Got's `timeout` is an object, http's `timeout` is a number, so they're not compatible. | ||
delete options.timeout; | ||
// Set cookies | ||
if (options.cookieJar) { | ||
const cookieString = await options.cookieJar.getCookieString(options.url.toString()); | ||
if (is_1.default.nonEmptyString(cookieString)) { | ||
options.headers.cookie = cookieString; | ||
} | ||
else { | ||
delete options.headers.cookie; | ||
} | ||
} | ||
// `http-cache-semantics` checks this | ||
delete options.url; | ||
return options; | ||
}; | ||
exports.reNormalizeArguments = (options) => exports.normalizeArguments(url_1.format(options), options); |
/// <reference types="node" /> | ||
import { IncomingMessage, ClientRequest } from 'http'; | ||
import { ClientRequest } from 'http'; | ||
import { Transform as TransformStream } from 'stream'; | ||
import EventEmitter = require('events'); | ||
export declare function downloadProgress(_response: IncomingMessage, emitter: EventEmitter, downloadBodySize?: number): TransformStream; | ||
export declare function downloadProgress(emitter: EventEmitter, downloadBodySize?: number): TransformStream; | ||
export declare function uploadProgress(request: ClientRequest, emitter: EventEmitter, uploadBodySize?: number): void; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const stream_1 = require("stream"); | ||
function downloadProgress(_response, emitter, downloadBodySize) { | ||
let downloaded = 0; | ||
return new stream_1.Transform({ | ||
const is_1 = require("@sindresorhus/is"); | ||
function downloadProgress(emitter, downloadBodySize) { | ||
let downloadedBytes = 0; | ||
const progressStream = new stream_1.Transform({ | ||
transform(chunk, _encoding, callback) { | ||
downloaded += chunk.length; | ||
const percent = downloadBodySize ? downloaded / downloadBodySize : 0; | ||
downloadedBytes += chunk.length; | ||
const percent = downloadBodySize ? downloadedBytes / downloadBodySize : 0; | ||
// Let `flush()` be responsible for emitting the last event | ||
@@ -14,3 +15,3 @@ if (percent < 1) { | ||
percent, | ||
transferred: downloaded, | ||
transferred: downloadedBytes, | ||
total: downloadBodySize | ||
@@ -24,3 +25,3 @@ }); | ||
percent: 1, | ||
transferred: downloaded, | ||
transferred: downloadedBytes, | ||
total: downloadBodySize | ||
@@ -31,2 +32,3 @@ }); | ||
}); | ||
return progressStream; | ||
} | ||
@@ -36,3 +38,3 @@ exports.downloadProgress = downloadProgress; | ||
const uploadEventFrequency = 150; | ||
let uploaded = 0; | ||
let uploadedBytes = 0; | ||
let progressInterval; | ||
@@ -47,2 +49,5 @@ emitter.emit('uploadProgress', { | ||
}); | ||
request.once('abort', () => { | ||
clearInterval(progressInterval); | ||
}); | ||
request.once('response', () => { | ||
@@ -52,3 +57,3 @@ clearInterval(progressInterval); | ||
percent: 1, | ||
transferred: uploaded, | ||
transferred: uploadedBytes, | ||
total: uploadBodySize | ||
@@ -60,15 +65,23 @@ }); | ||
progressInterval = setInterval(() => { | ||
const lastUploaded = uploaded; | ||
/* istanbul ignore next: see #490 (occurs randomly!) */ | ||
const headersSize = request._header ? Buffer.byteLength(request._header) : 0; | ||
uploaded = socket.bytesWritten - headersSize; | ||
const lastUploadedBytes = uploadedBytes; | ||
/* istanbul ignore next: future versions of Node may not have this property */ | ||
if (!is_1.default.string(request._header)) { | ||
clearInterval(progressInterval); | ||
const url = new URL('https://github.com/sindresorhus/got/issues/new'); | ||
url.searchParams.set('title', '`request._header` is not present'); | ||
url.searchParams.set('body', 'It causes `uploadProgress` to fail.'); | ||
console.warn('`request._header` is not present. Please report this as a bug:\n' + url.href); | ||
return; | ||
} | ||
const headersSize = Buffer.byteLength(request._header); | ||
uploadedBytes = socket.bytesWritten - headersSize; | ||
// Don't emit events with unchanged progress and | ||
// prevent last event from being emitted, because | ||
// it's emitted when `response` is emitted | ||
if (uploaded === lastUploaded || uploaded === uploadBodySize) { | ||
if (uploadedBytes === lastUploadedBytes || uploadedBytes === uploadBodySize) { | ||
return; | ||
} | ||
emitter.emit('uploadProgress', { | ||
percent: uploadBodySize ? uploaded / uploadBodySize : 0, | ||
transferred: uploaded, | ||
percent: uploadBodySize ? uploadedBytes / uploadBodySize : 0, | ||
transferred: uploadedBytes, | ||
total: uploadBodySize | ||
@@ -75,0 +88,0 @@ }); |
/// <reference types="node" /> | ||
import stream = require('stream'); | ||
import EventEmitter = require('events'); | ||
import { NormalizedOptions } from './utils/types'; | ||
export declare type GetMethodRedirectCodes = 300 | 301 | 302 | 303 | 304 | 305 | 307 | 308; | ||
export declare type AllMethodRedirectCodes = 300 | 303 | 307 | 308; | ||
export declare type WithoutBody = 'GET' | 'HEAD'; | ||
export interface RequestAsEventEmitter extends EventEmitter { | ||
@@ -12,3 +8,4 @@ retry: <T extends Error>(error: T) => boolean; | ||
} | ||
declare const _default: (options: NormalizedOptions, input?: stream.Transform) => RequestAsEventEmitter; | ||
declare const _default: (options: NormalizedOptions) => RequestAsEventEmitter; | ||
export default _default; | ||
export declare const proxyEvents: (proxy: any, emitter: any) => void; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const url_1 = require("url"); | ||
const util_1 = require("util"); | ||
const stream = require("stream"); | ||
const EventEmitter = require("events"); | ||
const http = require("http"); | ||
const https = require("https"); | ||
const CacheableRequest = require("cacheable-request"); | ||
const toReadableStream = require("to-readable-stream"); | ||
const is_1 = require("@sindresorhus/is"); | ||
const http_timer_1 = require("@szmarczak/http-timer"); | ||
const timed_out_1 = require("./utils/timed-out"); | ||
const get_body_size_1 = require("./utils/get-body-size"); | ||
const is_form_data_1 = require("./utils/is-form-data"); | ||
const calculate_retry_delay_1 = require("./calculate-retry-delay"); | ||
const get_response_1 = require("./get-response"); | ||
const normalize_arguments_1 = require("./normalize-arguments"); | ||
const progress_1 = require("./progress"); | ||
const errors_1 = require("./errors"); | ||
const url_to_options_1 = require("./utils/url-to-options"); | ||
const dynamic_require_1 = require("./utils/dynamic-require"); | ||
const getMethodRedirectCodes = new Set([300, 301, 302, 303, 304, 305, 307, 308]); | ||
const allMethodRedirectCodes = new Set([300, 303, 307, 308]); | ||
const withoutBody = new Set(['GET', 'HEAD']); | ||
exports.default = (options, input) => { | ||
const redirectCodes = new Set([300, 301, 302, 303, 304, 307, 308]); | ||
exports.default = (options) => { | ||
const emitter = new EventEmitter(); | ||
const requestURL = options.url.toString(); | ||
const redirects = []; | ||
let retryCount = 0; | ||
let currentRequest; | ||
let requestUrl; | ||
let redirectString; | ||
let uploadBodySize; | ||
let retryCount = 0; | ||
let shouldAbort = false; | ||
const setCookie = options.cookieJar ? util_1.promisify(options.cookieJar.setCookie.bind(options.cookieJar)) : null; | ||
const getCookieString = options.cookieJar ? util_1.promisify(options.cookieJar.getCookieString.bind(options.cookieJar)) : null; | ||
const agents = is_1.default.object(options.agent) ? options.agent : null; | ||
const emitError = async (error) => { | ||
@@ -49,34 +36,7 @@ try { | ||
}; | ||
const get = async (options) => { | ||
const currentUrl = redirectString || requestUrl; | ||
if (options.protocol !== 'http:' && options.protocol !== 'https:') { | ||
throw new errors_1.UnsupportedProtocolError(options); | ||
} | ||
decodeURI(currentUrl); | ||
let requestFn; | ||
if (is_1.default.function_(options.request)) { | ||
requestFn = options.request; | ||
} | ||
else { | ||
requestFn = options.protocol === 'https:' ? https.request : http.request; | ||
} | ||
if (agents) { | ||
const protocolName = options.protocol === 'https:' ? 'https' : 'http'; | ||
options.agent = agents[protocolName] || options.agent; | ||
} | ||
/* istanbul ignore next: electron.net is broken */ | ||
// No point in typing process.versions correctly, as | ||
// process.version.electron is used only once, right here. | ||
if (options.useElectronNet && process.versions.electron) { | ||
const electron = dynamic_require_1.default(module, 'electron'); // Trick webpack | ||
requestFn = electron.net.request || electron.remote.net.request; | ||
} | ||
if (options.cookieJar) { | ||
const cookieString = await getCookieString(currentUrl); | ||
if (is_1.default.nonEmptyString(cookieString)) { | ||
options.headers.cookie = cookieString; | ||
} | ||
} | ||
const get = async () => { | ||
let httpOptions = await normalize_arguments_1.normalizeRequestArguments(options); | ||
let timings; | ||
const handleResponse = async (response) => { | ||
var _a; | ||
try { | ||
@@ -97,5 +57,6 @@ /* istanbul ignore next: fixes https://github.com/electron/electron/blob/cbb460d47628a7a146adf4419ed48550a98b2923/lib/browser/api/net.js#L59-L65 */ | ||
const typedResponse = response; | ||
// This is intentionally using `||` over `??` so it can also catch empty status message. | ||
typedResponse.statusMessage = typedResponse.statusMessage || http.STATUS_CODES[statusCode]; | ||
typedResponse.url = currentUrl; | ||
typedResponse.requestUrl = requestUrl; | ||
typedResponse.url = options.url.toString(); | ||
typedResponse.requestUrl = requestURL; | ||
typedResponse.retryCount = retryCount; | ||
@@ -105,19 +66,20 @@ typedResponse.timings = timings; | ||
typedResponse.request = { options }; | ||
typedResponse.isFromCache = typedResponse.fromCache || false; | ||
typedResponse.isFromCache = (_a = typedResponse.fromCache, (_a !== null && _a !== void 0 ? _a : false)); | ||
delete typedResponse.fromCache; | ||
if (!typedResponse.isFromCache) { | ||
typedResponse.ip = response.connection.remoteAddress; | ||
// @ts-ignore Node.js typings haven't been updated yet | ||
typedResponse.ip = response.socket.remoteAddress; | ||
} | ||
const rawCookies = typedResponse.headers['set-cookie']; | ||
if (options.cookieJar && rawCookies) { | ||
let promises = rawCookies.map((rawCookie) => setCookie(rawCookie, typedResponse.url)); | ||
if (Reflect.has(options, 'cookieJar') && rawCookies) { | ||
let promises = rawCookies.map((rawCookie) => options.cookieJar.setCookie(rawCookie, typedResponse.url)); | ||
if (options.ignoreInvalidCookies) { | ||
promises = promises.map(async (p) => p.catch(() => { })); | ||
promises = promises.map(p => p.catch(() => { })); | ||
} | ||
await Promise.all(promises); | ||
} | ||
if (options.followRedirect && 'location' in typedResponse.headers) { | ||
if (allMethodRedirectCodes.has(statusCode) || (getMethodRedirectCodes.has(statusCode) && (options.method === 'GET' || options.method === 'HEAD'))) { | ||
typedResponse.resume(); // We're being redirected, we don't care about the response. | ||
if (statusCode === 303) { | ||
if (options.followRedirect && Reflect.has(typedResponse.headers, 'location') && redirectCodes.has(statusCode)) { | ||
typedResponse.resume(); // We're being redirected, we don't care about the response. | ||
if (statusCode === 303) { | ||
if (options.method !== 'GET' && options.method !== 'HEAD') { | ||
// Server responded with "see other", indicating that the resource exists at another location, | ||
@@ -127,24 +89,25 @@ // and the client should request it from that location via GET or HEAD. | ||
} | ||
if (redirects.length >= 10) { | ||
throw new errors_1.MaxRedirectsError(typedResponse, options); | ||
} | ||
// Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604 | ||
const redirectBuffer = Buffer.from(typedResponse.headers.location, 'binary').toString(); | ||
const redirectURL = new URL(redirectBuffer, currentUrl); | ||
redirectString = redirectURL.toString(); | ||
redirects.push(redirectString); | ||
const redirectOptions = { | ||
...options, | ||
port: undefined, | ||
auth: undefined, | ||
...url_to_options_1.default(redirectURL) | ||
}; | ||
for (const hook of options.hooks.beforeRedirect) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await hook(redirectOptions, typedResponse); | ||
} | ||
emitter.emit('redirect', response, redirectOptions); | ||
await get(redirectOptions); | ||
return; | ||
delete options.body; | ||
delete options.json; | ||
delete options.form; | ||
} | ||
if (redirects.length >= options.maxRedirects) { | ||
throw new errors_1.MaxRedirectsError(typedResponse, options.maxRedirects, options); | ||
} | ||
// Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604 | ||
const redirectBuffer = Buffer.from(typedResponse.headers.location, 'binary').toString(); | ||
const redirectURL = new URL(redirectBuffer, options.url); | ||
// Redirecting to a different site, clear cookies. | ||
if (redirectURL.hostname !== options.url.hostname) { | ||
delete options.headers.cookie; | ||
} | ||
redirects.push(redirectURL.toString()); | ||
options.url = redirectURL; | ||
for (const hook of options.hooks.beforeRedirect) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await hook(options, typedResponse); | ||
} | ||
emitter.emit('redirect', response, options); | ||
await get(); | ||
return; | ||
} | ||
@@ -163,7 +126,8 @@ get_response_1.default(typedResponse, options, emitter); | ||
currentRequest = request; | ||
// `request.aborted` is a boolean since v11.0.0: https://github.com/nodejs/node/commit/4b00c4fafaa2ae8c41c1f78823c0feb810ae4723#diff-e3bc37430eb078ccbafe3aa3b570c91a | ||
// We need to allow `TimedOutTimeoutError` here, because it `stream.pipeline(…)` aborts it automatically. | ||
const isAborted = () => typeof request.aborted === 'number' || request.aborted === true; | ||
const onError = (error) => { | ||
const isTimedOutError = error instanceof timed_out_1.TimeoutError; | ||
// `request.aborted` is a boolean since v11.0.0: https://github.com/nodejs/node/commit/4b00c4fafaa2ae8c41c1f78823c0feb810ae4723#diff-e3bc37430eb078ccbafe3aa3b570c91a | ||
// We need to allow `TimedOutTimeoutError` here, because it `stream.pipeline(..)` aborts it automatically. | ||
if (!isTimedOutError && (typeof request.aborted === 'number' || request.aborted === true)) { | ||
if (!isTimedOutError && isAborted()) { | ||
return; | ||
@@ -188,3 +152,3 @@ } | ||
// No need to attach an error handler here, | ||
// as `stream.pipeline(...)` doesn't remove this handler | ||
// as `stream.pipeline(…)` doesn't remove this handler | ||
// to allow stream reuse. | ||
@@ -194,22 +158,23 @@ request.emit('upload-complete'); | ||
request.on('error', onError); | ||
timings = http_timer_1.default(request); | ||
timings = http_timer_1.default(request); // TODO: Make `@szmarczak/http-timer` set `request.timings` and `response.timings` | ||
const uploadBodySize = httpOptions.headers['content-length'] ? Number(httpOptions.headers['content-length']) : undefined; | ||
progress_1.uploadProgress(request, emitter, uploadBodySize); | ||
if (options.gotTimeout) { | ||
timed_out_1.default(request, options.gotTimeout, options); | ||
timed_out_1.default(request, options.timeout, options.url); | ||
emitter.emit('request', request); | ||
if (isAborted()) { | ||
return; | ||
} | ||
emitter.emit('request', request); | ||
try { | ||
if (is_1.default.nodeStream(options.body)) { | ||
const { body } = options; | ||
delete options.body; | ||
// `stream.pipeline(...)` does it for us. | ||
request.removeListener('error', onError); | ||
stream.pipeline(body, request, uploadComplete); | ||
if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') { | ||
if (is_1.default.nodeStream(httpOptions.body)) { | ||
// `stream.pipeline(…)` handles `error` for us. | ||
request.removeListener('error', onError); | ||
stream.pipeline( | ||
// @ts-ignore Upgrade `@sindresorhus/is` | ||
httpOptions.body, request, uploadComplete); | ||
} | ||
else { | ||
request.end(httpOptions.body, uploadComplete); | ||
} | ||
} | ||
else if (options.body) { | ||
request.end(options.body, uploadComplete); | ||
} | ||
else if (input && (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH')) { | ||
stream.pipeline(input, request, uploadComplete); | ||
} | ||
else { | ||
@@ -224,4 +189,8 @@ request.end(uploadComplete); | ||
if (options.cache) { | ||
const cacheableRequest = new CacheableRequest(requestFn, options.cache); | ||
const cacheRequest = cacheableRequest(options, handleResponse); | ||
// `cacheable-request` doesn't support Node 10 API, fallback. | ||
httpOptions = { | ||
...httpOptions, | ||
...url_to_options_1.default(options.url) | ||
}; | ||
const cacheRequest = options.cacheableRequest(httpOptions, handleResponse); | ||
cacheRequest.once('error', error => { | ||
@@ -238,6 +207,7 @@ if (error instanceof CacheableRequest.RequestError) { | ||
else { | ||
// Catches errors thrown by calling requestFn(...) | ||
// Catches errors thrown by calling `requestFn(…)` | ||
try { | ||
// @ts-ignore TS complains that URLSearchParams is not the same as URLSearchParams | ||
handleRequest(requestFn(options, handleResponse)); | ||
// @ts-ignore 1. TS complains that URLSearchParams is not the same as URLSearchParams. | ||
// 2. It doesn't notice that `options.timeout` is deleted above. | ||
handleRequest(httpOptions.request(options.url, httpOptions, handleResponse)); | ||
} | ||
@@ -249,3 +219,3 @@ catch (error) { | ||
}; | ||
emitter.retry = (error) => { | ||
emitter.retry = error => { | ||
let backoff; | ||
@@ -277,3 +247,3 @@ retryCount++; | ||
} | ||
await get(options); | ||
await get(); | ||
} | ||
@@ -303,51 +273,3 @@ catch (error_) { | ||
} | ||
// Serialize body | ||
const { body, headers } = options; | ||
const isForm = !is_1.default.nullOrUndefined(options.form); | ||
const isJSON = !is_1.default.nullOrUndefined(options.json); | ||
const isBody = !is_1.default.nullOrUndefined(body); | ||
if ((isBody || isForm || isJSON) && withoutBody.has(options.method)) { | ||
throw new TypeError(`The \`${options.method}\` method cannot be used with a body`); | ||
} | ||
if (isBody) { | ||
if (isForm || isJSON) { | ||
throw new TypeError('The `body` option cannot be used with the `json` option or `form` option'); | ||
} | ||
if (is_1.default.object(body) && is_form_data_1.default(body)) { | ||
// Special case for https://github.com/form-data/form-data | ||
headers['content-type'] = headers['content-type'] || `multipart/form-data; boundary=${body.getBoundary()}`; | ||
} | ||
else if (!is_1.default.nodeStream(body) && !is_1.default.string(body) && !is_1.default.buffer(body)) { | ||
throw new TypeError('The `body` option must be a stream.Readable, string, Buffer, Object or Array'); | ||
} | ||
} | ||
else if (isForm) { | ||
if (!is_1.default.object(options.form)) { | ||
throw new TypeError('The `form` option must be an Object'); | ||
} | ||
headers['content-type'] = headers['content-type'] || 'application/x-www-form-urlencoded'; | ||
options.body = (new URLSearchParams(options.form)).toString(); | ||
} | ||
else if (isJSON) { | ||
headers['content-type'] = headers['content-type'] || 'application/json'; | ||
options.body = JSON.stringify(options.json); | ||
} | ||
// Convert buffer to stream to receive upload progress events (#322) | ||
if (is_1.default.buffer(body)) { | ||
options.body = toReadableStream(body); | ||
uploadBodySize = body.length; | ||
} | ||
else { | ||
uploadBodySize = await get_body_size_1.default(options); | ||
} | ||
if (is_1.default.undefined(headers['content-length']) && is_1.default.undefined(headers['transfer-encoding'])) { | ||
if ((uploadBodySize > 0 || options.method === 'PUT') && !is_1.default.undefined(uploadBodySize)) { | ||
headers['content-length'] = String(uploadBodySize); | ||
} | ||
} | ||
if (!options.stream && options.responseType === 'json' && is_1.default.undefined(headers.accept)) { | ||
options.headers.accept = 'application/json'; | ||
} | ||
requestUrl = options.href || (new URL(options.path, url_1.format(options))).toString(); | ||
await get(options); | ||
await get(); | ||
} | ||
@@ -360,1 +282,14 @@ catch (error) { | ||
}; | ||
exports.proxyEvents = (proxy, emitter) => { | ||
const events = [ | ||
'request', | ||
'redirect', | ||
'uploadProgress', | ||
'downloadProgress' | ||
]; | ||
for (const event of events) { | ||
emitter.on(event, (...args) => { | ||
proxy.emit(event, ...args); | ||
}); | ||
} | ||
}; |
"use strict"; | ||
/* istanbul ignore file: used for webpack */ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = (moduleObject, moduleId) => { | ||
return moduleObject.require(moduleId); | ||
}; | ||
exports.default = (moduleObject, moduleId) => moduleObject.require(moduleId); |
@@ -9,7 +9,7 @@ "use strict"; | ||
exports.default = async (options) => { | ||
const { body, headers, stream } = options; | ||
const { body, headers, isStream } = options; | ||
if (headers && 'content-length' in headers) { | ||
return Number(headers['content-length']); | ||
} | ||
if (!body && !stream) { | ||
if (!body && !isStream) { | ||
return 0; | ||
@@ -16,0 +16,0 @@ } |
@@ -24,2 +24,3 @@ "use strict"; | ||
const addTimeout = (delay, callback, ...args) => { | ||
var _a, _b; | ||
// Event loop order is timers, poll, immediates. | ||
@@ -30,10 +31,8 @@ // The timed event may emit during the current tick poll phase, so | ||
const timeout = setTimeout(() => { | ||
var _a, _b; | ||
// @ts-ignore https://github.com/microsoft/TypeScript/issues/26113 | ||
immediate = setImmediate(callback, delay, ...args); | ||
immediate.unref(); | ||
(_b = (_a = immediate).unref) === null || _b === void 0 ? void 0 : _b.call(_a); | ||
}, delay); | ||
/* istanbul ignore next: in order to support electron renderer */ | ||
if (timeout.unref) { | ||
timeout.unref(); | ||
} | ||
(_b = (_a = timeout).unref) === null || _b === void 0 ? void 0 : _b.call(_a); | ||
const cancel = () => { | ||
@@ -81,7 +80,9 @@ clearTimeout(timeout); | ||
once(request, 'socket', (socket) => { | ||
// TODO: There seems to not be a 'socketPath' on the request, but there IS a socket.remoteAddress | ||
var _a; | ||
// @ts-ignore Node typings doesn't have this property | ||
const { socketPath } = request; | ||
/* istanbul ignore next: hard to test */ | ||
if (socket.connecting) { | ||
if (typeof delays.lookup !== 'undefined' && !socketPath && !net.isIP(hostname || host || '') && typeof socket.address().address === 'undefined') { | ||
const hasPath = Boolean((socketPath !== null && socketPath !== void 0 ? socketPath : net.isIP((_a = (hostname !== null && hostname !== void 0 ? hostname : host), (_a !== null && _a !== void 0 ? _a : ''))) !== 0)); | ||
if (typeof delays.lookup !== 'undefined' && !hasPath && typeof socket.address().address === 'undefined') { | ||
const cancelTimeout = addTimeout(delays.lookup, timeoutHandler, 'lookup'); | ||
@@ -92,3 +93,3 @@ once(socket, 'lookup', cancelTimeout); | ||
const timeConnect = () => addTimeout(delays.connect, timeoutHandler, 'connect'); | ||
if (socketPath || net.isIP(hostname || host || '')) { | ||
if (hasPath) { | ||
once(socket, 'connect', timeConnect()); | ||
@@ -95,0 +96,0 @@ } |
@@ -6,6 +6,5 @@ /// <reference types="node" /> | ||
import { Readable as ReadableStream } from 'stream'; | ||
import { Except, Merge } from 'type-fest'; | ||
import PCancelable = require('p-cancelable'); | ||
import { CookieJar } from 'tough-cookie'; | ||
import { StorageAdapter } from 'cacheable-request'; | ||
import { Except } from 'type-fest'; | ||
import CacheableRequest = require('cacheable-request'); | ||
import CacheableLookup from 'cacheable-lookup'; | ||
@@ -17,10 +16,10 @@ import Keyv = require('keyv'); | ||
import { ProxyStream } from '../as-stream'; | ||
import { URLOptions } from './options-to-url'; | ||
export declare type GenericError = Error | GotError | ParseError | HTTPError | MaxRedirectsError; | ||
export declare type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'TRACE' | 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete' | 'options' | 'trace'; | ||
export declare type ErrorCode = 'ETIMEDOUT' | 'ECONNRESET' | 'EADDRINUSE' | 'ECONNREFUSED' | 'EPIPE' | 'ENOTFOUND' | 'ENETUNREACH' | 'EAI_AGAIN'; | ||
export declare type StatusCode = number; | ||
export declare type ResponseType = 'json' | 'buffer' | 'text'; | ||
export declare type URLArgument = string | https.RequestOptions | URL; | ||
export interface Response extends http.IncomingMessage { | ||
body: Buffer | string | any; | ||
statusCode: StatusCode; | ||
export declare type ResponseType = 'json' | 'buffer' | 'text' | 'default'; | ||
export interface Response<BodyType = unknown> extends http.IncomingMessage { | ||
body: BodyType; | ||
statusCode: number; | ||
/** | ||
@@ -44,3 +43,3 @@ The remote IP address. | ||
export interface ResponseObject extends ResponseLike { | ||
connection: { | ||
socket: { | ||
remoteAddress: string; | ||
@@ -51,4 +50,4 @@ }; | ||
attemptCount: number; | ||
retryOptions: NormalizedRetryOptions; | ||
error: Error | GotError | ParseError | HTTPError | MaxRedirectsError; | ||
retryOptions: RetryOptions; | ||
error: GenericError; | ||
computedValue: number; | ||
@@ -62,18 +61,10 @@ } | ||
methods?: Method[]; | ||
statusCodes?: StatusCode[]; | ||
statusCodes?: number[]; | ||
errorCodes?: string[]; | ||
maxRetryAfter?: number; | ||
} | ||
export interface NormalizedRetryOptions { | ||
limit: number; | ||
calculateDelay: RetryFunction; | ||
methods: ReadonlySet<Method>; | ||
statusCodes: ReadonlySet<number>; | ||
errorCodes: ReadonlySet<string>; | ||
maxRetryAfter: number; | ||
} | ||
export declare type RequestFunction = typeof https.request; | ||
export interface AgentByProtocol { | ||
http: http.Agent; | ||
https: https.Agent; | ||
http?: http.Agent; | ||
https?: https.Agent; | ||
} | ||
@@ -90,17 +81,19 @@ export interface Delays { | ||
export declare type Headers = Record<string, string | string[]>; | ||
export interface Options extends Except<https.RequestOptions, 'agent' | 'timeout' | 'host' | 'headers'> { | ||
host?: string; | ||
interface CookieJar { | ||
getCookieString(url: string, callback: (error: Error, cookieHeader: string) => void): void; | ||
getCookieString(url: string): Promise<string>; | ||
setCookie(rawCookie: string, url: string, callback: (error: Error, result: unknown) => void): void; | ||
setCookie(rawCookie: string, url: string): Promise<unknown>; | ||
} | ||
export interface Options extends URLOptions { | ||
url?: URL | string; | ||
body?: string | Buffer | ReadableStream; | ||
hostname?: string; | ||
path?: string; | ||
socketPath?: string; | ||
protocol?: string; | ||
href?: string; | ||
options?: Options; | ||
hooks?: Partial<Hooks>; | ||
decompress?: boolean; | ||
stream?: boolean; | ||
isStream?: boolean; | ||
encoding?: BufferEncoding | null; | ||
method?: Method; | ||
retry?: number | Partial<RetryOptions | NormalizedRetryOptions>; | ||
retry?: number | RetryOptions; | ||
throwHttpErrors?: boolean; | ||
@@ -111,4 +104,3 @@ cookieJar?: CookieJar; | ||
agent?: http.Agent | https.Agent | boolean | AgentByProtocol; | ||
gotTimeout?: number | Delays; | ||
cache?: string | StorageAdapter | false; | ||
cache?: string | CacheableRequest.StorageAdapter | false; | ||
headers?: Headers; | ||
@@ -120,6 +112,3 @@ responseType?: ResponseType; | ||
timeout?: number | Delays; | ||
dnsCache?: Map<string, string> | Keyv | false; | ||
url?: URL | string; | ||
searchParams?: Record<string, string | number | boolean | null> | URLSearchParams | string; | ||
query?: Options['searchParams']; | ||
dnsCache?: CacheableLookup | Map<string, string> | Keyv | false; | ||
useElectronNet?: boolean; | ||
@@ -131,15 +120,16 @@ form?: Record<string, any>; | ||
}; | ||
maxRedirects?: number; | ||
lookup?: CacheableLookup['lookup']; | ||
} | ||
export interface NormalizedOptions extends Except<Required<Options>, 'timeout' | 'dnsCache' | 'retry' | 'auth' | 'body' | 'port'> { | ||
export interface NormalizedOptions extends Except<Options, keyof URLOptions> { | ||
headers: Headers; | ||
hooks: Hooks; | ||
gotTimeout: Required<Delays>; | ||
retry: NormalizedRetryOptions; | ||
lookup?: CacheableLookup['lookup']; | ||
readonly prefixUrl: string; | ||
path: string; | ||
hostname: string; | ||
host: string; | ||
auth?: Options['auth']; | ||
body?: Options['body']; | ||
port?: Options['port']; | ||
timeout: Delays; | ||
dnsCache?: CacheableLookup | false; | ||
retry: Required<RetryOptions>; | ||
prefixUrl: string; | ||
method: Method; | ||
url: URL; | ||
cacheableRequest?: (options: string | URL | http.RequestOptions, callback?: (response: http.ServerResponse | ResponseLike) => void) => CacheableRequest.Emitter; | ||
path?: string; | ||
} | ||
@@ -151,14 +141,24 @@ export interface ExtendedOptions extends Options { | ||
export interface Defaults { | ||
options?: Options; | ||
handlers?: HandlerFunction[]; | ||
mutableDefaults?: boolean; | ||
options: Except<NormalizedOptions, 'url'>; | ||
handlers: HandlerFunction[]; | ||
_rawHandlers?: HandlerFunction[]; | ||
mutableDefaults: boolean; | ||
} | ||
export declare type URLOrOptions = URLArgument | (Options & { | ||
url: URLArgument; | ||
}); | ||
export interface CancelableRequest<T extends http.IncomingMessage | Buffer | string | object> extends PCancelable<T> { | ||
on(name: string, listener: () => void): CancelableRequest<T>; | ||
export declare type URLOrOptions = Options | string; | ||
export interface Progress { | ||
percent: number; | ||
transferred: number; | ||
total?: number; | ||
} | ||
export interface GotEvents<T> { | ||
on(name: 'request', listener: (request: http.ClientRequest) => void): T; | ||
on(name: 'response', listener: (response: Response) => void): T; | ||
on(name: 'redirect', listener: (response: Response, nextOptions: NormalizedOptions) => void): T; | ||
on(name: 'uploadProgress' | 'downloadProgress', listener: (progress: Progress) => void): T; | ||
} | ||
export interface CancelableRequest<T extends Response | Response['body']> extends Merge<PCancelable<T>, GotEvents<CancelableRequest<T>>> { | ||
json<TReturnType extends object>(): CancelableRequest<TReturnType>; | ||
buffer<TReturnType extends Buffer>(): CancelableRequest<TReturnType>; | ||
text<TReturnType extends string>(): CancelableRequest<TReturnType>; | ||
buffer(): CancelableRequest<Buffer>; | ||
text(): CancelableRequest<string>; | ||
} | ||
export {}; |
/// <reference types="node" /> | ||
import { UrlWithStringQuery } from 'url'; | ||
export interface URLOptions { | ||
export interface LegacyURLOptions { | ||
protocol: string; | ||
@@ -15,3 +15,3 @@ hostname: string; | ||
} | ||
declare const _default: (url: URL | UrlWithStringQuery) => URLOptions; | ||
declare const _default: (url: URL | UrlWithStringQuery) => LegacyURLOptions; | ||
export default _default; |
{ | ||
"name": "got", | ||
"version": "10.0.0-alpha.3.2", | ||
"version": "10.0.0-beta.1", | ||
"description": "Simplified HTTP requests", | ||
@@ -12,8 +12,9 @@ "license": "MIT", | ||
"scripts": { | ||
"test": "xo && nyc ava && tsc --noEmit", | ||
"test": "xo && tsc --noEmit && nyc ava", | ||
"release": "np", | ||
"build": "del-cli dist && tsc" | ||
"build": "del-cli dist && tsc", | ||
"prepare": "npm run build" | ||
}, | ||
"files": [ | ||
"dist" | ||
"dist/source" | ||
], | ||
@@ -41,3 +42,3 @@ "keywords": [ | ||
"@sindresorhus/is": "^1.0.0", | ||
"@szmarczak/http-timer": "^2.0.0", | ||
"@szmarczak/http-timer": "^3.1.0", | ||
"@types/cacheable-request": "^6.0.1", | ||
@@ -60,6 +61,6 @@ "@types/tough-cookie": "^2.3.5", | ||
"@types/duplexer3": "^0.1.0", | ||
"@types/node": "^12.7.5", | ||
"@types/node": "^12.12.8", | ||
"@types/sinon": "^7.0.13", | ||
"@typescript-eslint/eslint-plugin": "^2.3.1", | ||
"@typescript-eslint/parser": "^2.3.1", | ||
"@typescript-eslint/eslint-plugin": "^2.7.0", | ||
"@typescript-eslint/parser": "^2.7.0", | ||
"ava": "^2.4.0", | ||
@@ -70,9 +71,9 @@ "coveralls": "^3.0.4", | ||
"delay": "^4.3.0", | ||
"eslint-config-xo-typescript": "^0.19.0", | ||
"form-data": "^2.5.1", | ||
"eslint-config-xo-typescript": "^0.21.0", | ||
"form-data": "^3.0.0", | ||
"get-port": "^5.0.0", | ||
"keyv": "^3.1.0", | ||
"keyv": "^4.0.0", | ||
"lolex": "^5.1.1", | ||
"nock": "^11.3.4", | ||
"np": "^5.0.3", | ||
"np": "^5.1.3", | ||
"nyc": "^14.0.0", | ||
@@ -85,4 +86,4 @@ "p-event": "^4.0.0", | ||
"tough-cookie": "^3.0.0", | ||
"ts-node": "^8.4.1", | ||
"typescript": "^3.6.4", | ||
"ts-node": "^8.5.2", | ||
"typescript": "^3.7.2", | ||
"xo": "^0.25.3" | ||
@@ -122,5 +123,3 @@ }, | ||
"@typescript-eslint/explicit-function-return-type": "off", | ||
"@typescript-eslint/promise-function-async": "off", | ||
"@typescript-eslint/strict-boolean-expressions": "off", | ||
"@typescript-eslint/no-unnecessary-condition": "off" | ||
"@typescript-eslint/promise-function-async": "off" | ||
}, | ||
@@ -127,0 +126,0 @@ "ignores": [ |
186
readme.md
@@ -111,3 +111,3 @@ <div align="center"> | ||
Returns a Promise for a [`response` object](#response) or a [stream](#streams-1) if `options.stream` is set to true. | ||
Returns a Promise for a [`response` object](#response) or a [stream](#streams-1) if `options.isStream` is set to true. | ||
@@ -142,2 +142,4 @@ ##### url | ||
**Tip:** If the input URL still contains the initial `prefixUrl`, you can change it as many times as you want. Otherwise it will throw an error. | ||
```js | ||
@@ -149,2 +151,17 @@ const got = require('got'); | ||
//=> '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' | ||
})(); | ||
@@ -160,5 +177,5 @@ ``` | ||
Existing headers will be overwritten. Headers set to `null` will be omitted. | ||
Existing headers will be overwritten. Headers set to `undefined` will be omitted. | ||
###### stream | ||
###### isStream | ||
@@ -232,8 +249,15 @@ Type: `boolean`<br> | ||
Type: `string`<br> | ||
Default: `text` | ||
Default: `'default'` | ||
**Note:** When using streams, this option is ignored. | ||
Parsing method used to retrieve the body from the response. Can be `text`, `json` or `buffer`. The promise has `.json()` and `.buffer()` and `.text()` functions which set this option automatically. | ||
Parsing method used to retrieve the body from the response. | ||
- `'default'` - if `options.encoding` is `null`, the body will be a Buffer. Otherwise it will be a string unless it's overwritten in a `afterResponse` hook, | ||
- `'text'` - will always give a string, no matter what's the `options.encoding` or if the body is a custom object, | ||
- `'json'` - will always give an object, unless it's invalid JSON - then it will throw. | ||
- `'buffer'` - will always give a Buffer, no matter what's the `options.encoding`. It will throw if the body is a custom object. | ||
The promise has `.json()` and `.buffer()` and `.text()` functions which set this option automatically. | ||
Example: | ||
@@ -254,8 +278,20 @@ | ||
Type: [`tough.CookieJar` instance](https://github.com/salesforce/tough-cookie#cookiejar) | ||
Type: `object` | [`tough.CookieJar` instance](https://github.com/salesforce/tough-cookie#cookiejar) | ||
**Note:** If you provide this option, `options.headers.cookie` will be overridden. | ||
Cookie support. You don't have to care about parsing or how to store them. [Example.](#cookies) | ||
Cookie support. You don't have to care about parsing or how to store them. [Example](#cookies). | ||
###### cookieJar.setCookie | ||
Type: `Function<Promise>` | ||
The function takes two arguments: `rawCookie` (`string`) and `url` (`string`). | ||
###### cookieJar.getCookieString | ||
Type: `Function<Promise>` | ||
The function takes one argument: `url` (`string`). | ||
###### ignoreInvalidCookies | ||
@@ -289,4 +325,2 @@ | ||
**Note:** The `query` option was renamed to `searchParams` in Got v10. The `query` option name is still functional, but is being deprecated and will be completely removed in Got v11. | ||
Query string that will be added to the request URL. This will override the query string in `url`. | ||
@@ -350,3 +384,3 @@ | ||
**Note:** When using streams, this option is ignored. | ||
**Note:** When using streams, this option is ignored. If the connection is reset when downloading, you need to catch the error and clear the file you were writing into to prevent duplicated content. | ||
@@ -379,2 +413,11 @@ If `maxRetryAfter` is set to `undefined`, it will use `options.timeout`.<br> | ||
This supports [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 that request to the new location. | ||
###### maxRedirects | ||
Type: `number`<br> | ||
Default: `10` | ||
If exceeded, the request will be aborted and a `MaxRedirectsError` will be thrown. | ||
###### decompress | ||
@@ -501,2 +544,4 @@ | ||
**Note:** When using streams, this hook is ignored. | ||
Called with [normalized](source/normalize-arguments.ts) [request options](#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. Example: | ||
@@ -520,2 +565,4 @@ | ||
**Note:** When retrying in a `afterResponse` hook, all remaining `beforeRetry` hooks will be called without the `error` and `retryCount` arguments. | ||
###### hooks.afterResponse | ||
@@ -526,4 +573,6 @@ | ||
Called with [response object](#response) and a retry function. | ||
**Note:** When using streams, this hook is ignored. | ||
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. Example: | ||
@@ -555,2 +604,7 @@ | ||
} | ||
], | ||
beforeRetry: [ | ||
(options, error, retryCount) => { | ||
// This will be called on `retryWithMergedOptions(...)` | ||
} | ||
] | ||
@@ -680,3 +734,3 @@ }, | ||
Sets `options.stream` to `true`. | ||
Sets `options.isStream` to `true`. | ||
@@ -793,4 +847,2 @@ Returns a [duplex stream](https://nodejs.org/api/stream.html#stream_class_stream_duplex) with additional events: | ||
**Tip:** Need more control over the behavior of Got? Check out the [`got.create()`](documentation/advanced-creation.md). | ||
Additionally, `got.extend()` accepts two properties from the `defaults` object: `mutableDefaults` and `handlers`. Example: | ||
@@ -813,2 +865,30 @@ | ||
**Note:** Handlers can be asynchronous. The recommended approach is: | ||
```js | ||
const handler = (options, next) => { | ||
if (options.stream) { | ||
// It's a Stream | ||
return next(options); | ||
} | ||
// It's a Promise | ||
return (async () => { | ||
try { | ||
const response = await next(options); | ||
response.yourOwnProperty = true; | ||
return response; | ||
} catch (error) { | ||
// Every error will be replaced by this one. | ||
// Before you receive any error here, | ||
// it will be passed to the `beforeError` hooks first. | ||
// Note: this one won't be passed to `beforeError` hook. It's final. | ||
throw new Error('Your very own error.'); | ||
} | ||
})(); | ||
}; | ||
const instance = got.extend({handlers: [handler]}); | ||
``` | ||
#### got.extend(...instances) | ||
@@ -868,4 +948,54 @@ | ||
The default Got options used in that instance. | ||
The Got defaults used in that instance. | ||
##### [options](#options) | ||
##### handlers | ||
Type: `Function[]`<br> | ||
Default: `[]` | ||
An array of functions. You execute them directly by calling `got()`. They are some sort of "global hooks" - these functions are called first. The last handler (*it's hidden*) is either [`asPromise`](source/as-promise.ts) or [`asStream`](source/as-stream.ts), depending on the `options.isStream` property. | ||
Each handler takes two arguments: | ||
###### [options](#options) | ||
###### next() | ||
Returns a `Promise` or a `Stream` depending on [`options.isStream`](#isstream). | ||
```js | ||
const settings = { | ||
handlers: [ | ||
(options, next) => { | ||
if (options.isStream) { | ||
// It's a Stream, so we can perform stream-specific actions on it | ||
return next(options) | ||
.on('request', request => { | ||
setTimeout(() => { | ||
request.abort(); | ||
}, 50); | ||
}); | ||
} | ||
// It's a Promise | ||
return next(options); | ||
} | ||
], | ||
options: got.mergeOptions(got.defaults.options, { | ||
responseType: 'json' | ||
}) | ||
}; | ||
const jsonGot = got.create(settings); | ||
``` | ||
##### mutableDefaults | ||
Type: `boolean`<br> | ||
Default: `false` | ||
A read-only boolean describing whether the defaults are mutable or not. If set to `true`, you can [update headers over time](#hooksafterresponse), for example, update an access token when it expires. | ||
## Errors | ||
@@ -940,2 +1070,24 @@ | ||
When using hooks, simply throw an error to abort the request. | ||
```js | ||
(async () => { | ||
const request = got(url, { | ||
hooks: { | ||
beforeRequest: [ | ||
() => { | ||
throw new Error('Oops. Request canceled.'); | ||
} | ||
] | ||
} | ||
}); | ||
try { | ||
await request; | ||
} catch (error) { | ||
// … | ||
} | ||
})(); | ||
``` | ||
<a name="cache-adapters"></a> | ||
@@ -1215,3 +1367,3 @@ ## Cache | ||
It's a good idea to set the `'user-agent'` header so the provider can more easily see how their resource is used. By default, it's the URL to this repo. You can omit this header by setting it to `null`. | ||
It's a good idea to set the `'user-agent'` header so the provider can more easily see how their resource is used. By default, it's the URL to this repo. You can omit this header by setting it to `undefined`. | ||
@@ -1230,3 +1382,3 @@ ```js | ||
headers: { | ||
'user-agent': null | ||
'user-agent': undefined | ||
} | ||
@@ -1233,0 +1385,0 @@ }); |
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
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
149298
2132
1646
2
47
5
+ Added@szmarczak/http-timer@3.1.1(transitive)
- Removed@szmarczak/http-timer@2.1.0(transitive)
Updated@szmarczak/http-timer@^3.1.0