Initial commit

This commit is contained in:
jiapengyu
2026-07-27 13:37:48 +08:00
commit ecba71e2a5
39123 changed files with 5989154 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
import { CancelableRequest, NormalizedOptions } from './types';
export declare function createRejection(error: Error): CancelableRequest<never>;
export default function asPromise<T>(options: NormalizedOptions): CancelableRequest<T>;
+152
View File
@@ -0,0 +1,152 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const EventEmitter = require("events");
const getStream = require("get-stream");
const PCancelable = require("p-cancelable");
const is_1 = require("@sindresorhus/is");
const errors_1 = require("./errors");
const normalize_arguments_1 = require("./normalize-arguments");
const request_as_event_emitter_1 = require("./request-as-event-emitter");
const parseBody = (body, responseType, encoding) => {
if (responseType === 'json') {
return body.length === 0 ? '' : JSON.parse(body.toString());
}
if (responseType === 'buffer') {
return Buffer.from(body);
}
if (responseType === 'text') {
return body.toString(encoding);
}
throw new TypeError(`Unknown body type '${responseType}'`);
};
function createRejection(error) {
const promise = Promise.reject(error);
const returnPromise = () => promise;
promise.json = returnPromise;
promise.text = returnPromise;
promise.buffer = returnPromise;
promise.on = returnPromise;
return promise;
}
exports.createRejection = createRejection;
function asPromise(options) {
const proxy = new EventEmitter();
let body;
const promise = new PCancelable((resolve, reject, onCancel) => {
const emitter = request_as_event_emitter_1.default(options);
onCancel(emitter.abort);
const emitError = async (error) => {
try {
for (const hook of options.hooks.beforeError) {
// eslint-disable-next-line no-await-in-loop
error = await hook(error);
}
reject(error);
}
catch (error_) {
reject(error_);
}
};
emitter.on('response', async (response) => {
var _a;
proxy.emit('response', response);
// Download body
try {
body = await getStream.buffer(response, { encoding: 'binary' });
}
catch (error) {
emitError(new errors_1.ReadError(error, options));
return;
}
if ((_a = response.req) === null || _a === void 0 ? void 0 : _a.aborted) {
// Canceled while downloading - will throw a `CancelError` or `TimeoutError` error
return;
}
const isOk = () => {
const { statusCode } = response;
const limitStatusCode = options.followRedirect ? 299 : 399;
return (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304;
};
// Parse body
try {
response.body = parseBody(body, options.responseType, options.encoding);
}
catch (error) {
// Fall back to `utf8`
response.body = body.toString();
if (isOk()) {
const parseError = new errors_1.ParseError(error, response, options);
emitError(parseError);
return;
}
}
try {
for (const [index, hook] of options.hooks.afterResponse.entries()) {
// @ts-ignore 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 = normalize_arguments_1.normalizeArguments(normalize_arguments_1.mergeOptions(options, {
...updatedOptions,
retry: {
calculateDelay: () => 0
},
throwHttpErrors: false,
resolveBodyOnly: false
}));
// 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 = options.hooks.afterResponse.slice(0, index);
for (const hook of options.hooks.beforeRetry) {
// eslint-disable-next-line no-await-in-loop
await hook(typedOptions);
}
const promise = asPromise(typedOptions);
onCancel(() => {
promise.catch(() => { });
promise.cancel();
});
return promise;
});
}
}
catch (error) {
emitError(error);
return;
}
// Check for HTTP error codes
if (!isOk()) {
const error = new errors_1.HTTPError(response, options);
if (emitter.retry(error)) {
return;
}
if (options.throwHttpErrors) {
emitError(error);
return;
}
}
resolve(options.resolveBodyOnly ? response.body : response);
});
emitter.once('error', reject);
request_as_event_emitter_1.proxyEvents(proxy, emitter);
});
promise.on = (name, fn) => {
proxy.on(name, fn);
return promise;
};
const shortcut = (responseType) => {
// eslint-disable-next-line promise/prefer-await-to-then
const newPromise = promise.then(() => parseBody(body, responseType, options.encoding));
Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise));
return newPromise;
};
promise.json = () => {
if (is_1.default.undefined(body) && is_1.default.undefined(options.headers.accept)) {
options.headers.accept = 'application/json';
}
return shortcut('json');
};
promise.buffer = () => shortcut('buffer');
promise.text = () => shortcut('text');
return promise;
}
exports.default = asPromise;
+7
View File
@@ -0,0 +1,7 @@
/// <reference types="node" />
import { Duplex as DuplexStream } from 'stream';
import { GotEvents, NormalizedOptions } from './types';
export declare class ProxyStream<T = unknown> extends DuplexStream implements GotEvents<ProxyStream<T>> {
isFromCache?: boolean;
}
export default function asStream<T>(options: NormalizedOptions): ProxyStream<T>;
+121
View File
@@ -0,0 +1,121 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const duplexer3 = require("duplexer3");
const http_1 = require("http");
const stream_1 = require("stream");
const errors_1 = require("./errors");
const request_as_event_emitter_1 = require("./request-as-event-emitter");
class ProxyStream extends stream_1.Duplex {
}
exports.ProxyStream = ProxyStream;
function asStream(options) {
const input = new stream_1.PassThrough();
const output = new stream_1.PassThrough();
const proxy = duplexer3(input, output);
const piped = new Set();
let isFinished = false;
options.retry.calculateDelay = () => 0;
if (options.body || options.json || options.form) {
proxy.write = () => {
proxy.destroy();
throw new Error('Got\'s stream is not writable when the `body`, `json` or `form` option is used');
};
}
else if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH' || (options.allowGetBody && options.method === 'GET')) {
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) => {
try {
for (const hook of options.hooks.beforeError) {
// eslint-disable-next-line no-await-in-loop
error = await hook(error);
}
proxy.emit('error', error);
}
catch (error_) {
proxy.emit('error', error_);
}
};
// Cancels the request
proxy._destroy = (error, callback) => {
callback(error);
emitter.abort();
};
emitter.on('response', (response) => {
const { statusCode, isFromCache } = response;
proxy.isFromCache = isFromCache;
if (options.throwHttpErrors && statusCode !== 304 && (statusCode < 200 || statusCode > 299)) {
emitError(new errors_1.HTTPError(response, options));
return;
}
{
const read = proxy._read;
proxy._read = (...args) => {
isFinished = true;
proxy._read = read;
return read.apply(proxy, args);
};
}
if (options.encoding) {
proxy.setEncoding(options.encoding);
}
// We cannot use `stream.pipeline(...)` here,
// because if we did then `output` would throw
// the original error before throwing `ReadError`.
response.pipe(output);
response.once('error', error => {
emitError(new errors_1.ReadError(error, options));
});
for (const destination of piped) {
if (destination.headersSent) {
continue;
}
for (const [key, value] of Object.entries(response.headers)) {
// Got gives *decompressed* data. Overriding `content-encoding` header would result in an error.
// It's not possible to decompress already decompressed data, is it?
const isAllowed = options.decompress ? key !== 'content-encoding' : true;
if (isAllowed) {
destination.setHeader(key, value);
}
}
destination.statusCode = response.statusCode;
}
proxy.emit('response', response);
});
request_as_event_emitter_1.proxyEvents(proxy, emitter);
emitter.on('error', (error) => proxy.emit('error', error));
const pipe = proxy.pipe.bind(proxy);
const unpipe = proxy.unpipe.bind(proxy);
proxy.pipe = (destination, options) => {
if (isFinished) {
throw new Error('Failed to pipe. The response has been emitted already.');
}
pipe(destination, options);
if (destination instanceof http_1.ServerResponse) {
piped.add(destination);
}
return destination;
};
proxy.unpipe = stream => {
piped.delete(stream);
return unpipe(stream);
};
proxy.on('pipe', source => {
if (source instanceof http_1.IncomingMessage) {
options.headers = {
...source.headers,
...options.headers
};
}
});
proxy.isFromCache = undefined;
return proxy;
}
exports.default = asStream;
+3
View File
@@ -0,0 +1,3 @@
import { RetryFunction } from './types';
declare const calculateRetryDelay: RetryFunction;
export default calculateRetryDelay;
+39
View File
@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = require("@sindresorhus/is");
const errors_1 = require("./errors");
const retryAfterStatusCodes = new Set([413, 429, 503]);
const isErrorWithResponse = (error) => (error instanceof errors_1.HTTPError || error instanceof errors_1.ParseError || error instanceof errors_1.MaxRedirectsError);
const calculateRetryDelay = ({ attemptCount, retryOptions, error }) => {
if (attemptCount > retryOptions.limit) {
return 0;
}
const hasMethod = retryOptions.methods.includes(error.options.method);
const hasErrorCode = Reflect.has(error, 'code') && retryOptions.errorCodes.includes(error.code);
const hasStatusCode = isErrorWithResponse(error) && retryOptions.statusCodes.includes(error.response.statusCode);
if (!hasMethod || (!hasErrorCode && !hasStatusCode)) {
return 0;
}
if (isErrorWithResponse(error)) {
const { response } = error;
if (response && Reflect.has(response.headers, 'retry-after') && retryAfterStatusCodes.has(response.statusCode)) {
let after = Number(response.headers['retry-after']);
if (is_1.default.nan(after)) {
after = Date.parse(response.headers['retry-after']) - Date.now();
}
else {
after *= 1000;
}
if (after > retryOptions.maxRetryAfter) {
return 0;
}
return after;
}
if (response.statusCode === 413) {
return 0;
}
}
const noise = Math.random() * 100;
return ((2 ** (attemptCount - 1)) * 1000) + noise;
};
exports.default = calculateRetryDelay;
+83
View File
@@ -0,0 +1,83 @@
/// <reference types="node" />
import { Merge, Except } from 'type-fest';
import { ProxyStream } from './as-stream';
import * as errors from './errors';
import { CancelableRequest, Defaults, ExtendOptions, HandlerFunction, NormalizedOptions, Options, Response, URLOrOptions, PaginationOptions } from './types';
export declare type HTTPAlias = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete';
export declare type ReturnStream = <T>(url: string | Merge<Options, {
isStream?: true;
}>, options?: Merge<Options, {
isStream?: true;
}>) => ProxyStream<T>;
export declare type GotReturn<T = unknown> = CancelableRequest<T> | ProxyStream<T>;
export declare type OptionsOfDefaultResponseBody = Merge<Options, {
isStream?: false;
resolveBodyOnly?: false;
responseType?: 'default';
}>;
declare type OptionsOfTextResponseBody = Merge<Options, {
isStream?: false;
resolveBodyOnly?: false;
responseType: 'text';
}>;
declare type OptionsOfJSONResponseBody = Merge<Options, {
isStream?: false;
resolveBodyOnly?: false;
responseType: 'json';
}>;
declare type OptionsOfBufferResponseBody = Merge<Options, {
isStream?: false;
resolveBodyOnly?: false;
responseType: 'buffer';
}>;
declare type ResponseBodyOnly = {
resolveBodyOnly: true;
};
/**
Can be used to match methods explicitly or parameters extraction: `Parameters<GotRequestMethod>`.
*/
export interface GotRequestMethod {
<T = string>(url: string | OptionsOfDefaultResponseBody, options?: OptionsOfDefaultResponseBody): CancelableRequest<Response<T>>;
(url: string | OptionsOfTextResponseBody, options?: OptionsOfTextResponseBody): CancelableRequest<Response<string>>;
<T>(url: string | OptionsOfJSONResponseBody, options?: OptionsOfJSONResponseBody): CancelableRequest<Response<T>>;
(url: string | OptionsOfBufferResponseBody, options?: OptionsOfBufferResponseBody): CancelableRequest<Response<Buffer>>;
<T = string>(url: string | Merge<OptionsOfDefaultResponseBody, ResponseBodyOnly>, options?: Merge<OptionsOfDefaultResponseBody, ResponseBodyOnly>): CancelableRequest<T>;
(url: string | Merge<OptionsOfTextResponseBody, ResponseBodyOnly>, options?: Merge<OptionsOfTextResponseBody, ResponseBodyOnly>): CancelableRequest<string>;
<T>(url: string | Merge<OptionsOfJSONResponseBody, ResponseBodyOnly>, options?: Merge<OptionsOfJSONResponseBody, ResponseBodyOnly>): CancelableRequest<T>;
(url: string | Merge<OptionsOfBufferResponseBody, ResponseBodyOnly>, options?: Merge<OptionsOfBufferResponseBody, ResponseBodyOnly>): CancelableRequest<Buffer>;
<T>(url: string | Merge<Options, {
isStream: true;
}>, options?: Merge<Options, {
isStream: true;
}>): ProxyStream<T>;
}
export declare type GotPaginateOptions<T> = Except<Options, keyof PaginationOptions<unknown>> & PaginationOptions<T>;
export declare type URLOrGotPaginateOptions<T> = string | GotPaginateOptions<T>;
export interface GotPaginate {
<T>(url: URLOrGotPaginateOptions<T>, options?: GotPaginateOptions<T>): AsyncIterableIterator<T>;
all<T>(url: URLOrGotPaginateOptions<T>, options?: GotPaginateOptions<T>): Promise<T[]>;
}
export interface Got extends Record<HTTPAlias, GotRequestMethod>, GotRequestMethod {
stream: GotStream;
paginate: GotPaginate;
defaults: Defaults;
GotError: typeof errors.GotError;
CacheError: typeof errors.CacheError;
RequestError: typeof errors.RequestError;
ReadError: typeof errors.ReadError;
ParseError: typeof errors.ParseError;
HTTPError: typeof errors.HTTPError;
MaxRedirectsError: typeof errors.MaxRedirectsError;
UnsupportedProtocolError: typeof errors.UnsupportedProtocolError;
TimeoutError: typeof errors.TimeoutError;
CancelError: typeof errors.CancelError;
extend(...instancesOrOptions: Array<Got | ExtendOptions>): Got;
mergeInstances(parent: Got, ...instances: Got[]): Got;
mergeOptions(...sources: Options[]): NormalizedOptions;
}
export interface GotStream extends Record<HTTPAlias, ReturnStream> {
(url: URLOrOptions, options?: Options): ProxyStream;
}
export declare const defaultHandler: HandlerFunction;
declare const create: (defaults: Defaults) => Got;
export default create;
+153
View File
@@ -0,0 +1,153 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = require("@sindresorhus/is");
const as_promise_1 = require("./as-promise");
const as_stream_1 = require("./as-stream");
const errors = require("./errors");
const normalize_arguments_1 = require("./normalize-arguments");
const deep_freeze_1 = require("./utils/deep-freeze");
const getPromiseOrStream = (options) => options.isStream ? as_stream_1.default(options) : as_promise_1.default(options);
const isGotInstance = (value) => (Reflect.has(value, 'defaults') && Reflect.has(value.defaults, 'options'));
const aliases = [
'get',
'post',
'put',
'patch',
'head',
'delete'
];
exports.defaultHandler = (options, next) => next(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;
}));
// @ts-ignore Because the for loop handles it for us, as well as the other Object.defines
const got = (url, options) => {
var _a;
let iteration = 0;
const iterateHandlers = (newOptions) => {
return defaults.handlers[iteration++](newOptions, iteration === defaults.handlers.length ? getPromiseOrStream : iterateHandlers);
};
/* eslint-disable @typescript-eslint/return-await */
try {
return iterateHandlers(normalize_arguments_1.normalizeArguments(url, options, defaults));
}
catch (error) {
if ((_a = options) === null || _a === void 0 ? void 0 : _a.isStream) {
throw error;
}
else {
// @ts-ignore It's an Error not a response, but TS thinks it's calling .resolve
return as_promise_1.createRejection(error);
}
}
/* eslint-enable @typescript-eslint/return-await */
};
got.extend = (...instancesOrOptions) => {
const optionsArray = [defaults.options];
let handlers = [...defaults._rawHandlers];
let isMutableDefaults;
for (const value of instancesOrOptions) {
if (isGotInstance(value)) {
optionsArray.push(value.defaults.options);
handlers.push(...value.defaults._rawHandlers);
isMutableDefaults = value.defaults.mutableDefaults;
}
else {
optionsArray.push(value);
if (Reflect.has(value, 'handlers')) {
handlers.push(...value.handlers);
}
isMutableDefaults = value.mutableDefaults;
}
}
handlers = handlers.filter(handler => handler !== exports.defaultHandler);
if (handlers.length === 0) {
handlers.push(exports.defaultHandler);
}
return create({
options: normalize_arguments_1.mergeOptions(...optionsArray),
handlers,
mutableDefaults: Boolean(isMutableDefaults)
});
};
// @ts-ignore The missing methods because the for-loop handles it for us
got.stream = (url, options) => got(url, { ...options, isStream: true });
for (const method of aliases) {
// @ts-ignore Cannot properly type a function with multiple definitions yet
got[method] = (url, options) => got(url, { ...options, method });
got.stream[method] = (url, options) => got.stream(url, { ...options, method });
}
// @ts-ignore The missing property is added below
got.paginate = async function* (url, options) {
let normalizedOptions = normalize_arguments_1.normalizeArguments(url, options, defaults);
const pagination = normalizedOptions._pagination;
if (!is_1.default.object(pagination)) {
throw new Error('`options._pagination` must be implemented');
}
const all = [];
while (true) {
// @ts-ignore See https://github.com/sindresorhus/got/issues/954
// eslint-disable-next-line no-await-in-loop
const result = await got(normalizedOptions);
// eslint-disable-next-line no-await-in-loop
const parsed = await pagination.transform(result);
const current = [];
for (const item of parsed) {
if (pagination.filter(item, all, current)) {
if (!pagination.shouldContinue(item, all, current)) {
return;
}
yield item;
all.push(item);
current.push(item);
if (all.length === pagination.countLimit) {
return;
}
}
}
const optionsToMerge = pagination.paginate(result, all, current);
if (optionsToMerge === false) {
return;
}
if (optionsToMerge !== undefined) {
normalizedOptions = normalize_arguments_1.normalizeArguments(normalizedOptions, optionsToMerge);
}
}
};
got.paginate.all = async (url, options) => {
const results = [];
for await (const item of got.paginate(url, options)) {
results.push(item);
}
return results;
};
Object.assign(got, { ...errors, mergeOptions: normalize_arguments_1.mergeOptions });
Object.defineProperty(got, 'defaults', {
value: defaults.mutableDefaults ? defaults : deep_freeze_1.default(defaults),
writable: defaults.mutableDefaults,
configurable: defaults.mutableDefaults,
enumerable: true
});
return got;
};
exports.default = create;
+41
View File
@@ -0,0 +1,41 @@
import { Timings } from '@szmarczak/http-timer';
import { TimeoutError as TimedOutError } from './utils/timed-out';
import { Response, NormalizedOptions } from './types';
export declare class GotError extends Error {
code?: string;
stack: string;
readonly options: NormalizedOptions;
constructor(message: string, error: Partial<Error & {
code?: string;
}>, options: NormalizedOptions);
}
export declare class CacheError extends GotError {
constructor(error: Error, options: NormalizedOptions);
}
export declare class RequestError extends GotError {
constructor(error: Error, options: NormalizedOptions);
}
export declare class ReadError extends GotError {
constructor(error: Error, options: NormalizedOptions);
}
export declare class ParseError extends GotError {
readonly response: Response;
constructor(error: Error, response: Response, options: NormalizedOptions);
}
export declare class HTTPError extends GotError {
readonly response: Response;
constructor(response: Response, options: NormalizedOptions);
}
export declare class MaxRedirectsError extends GotError {
readonly response: Response;
constructor(response: Response, maxRedirects: number, options: NormalizedOptions);
}
export declare class UnsupportedProtocolError extends GotError {
constructor(options: NormalizedOptions);
}
export declare class TimeoutError extends GotError {
timings: Timings;
event: string;
constructor(error: TimedOutError, timings: Timings, options: NormalizedOptions);
}
export { CancelError } from 'p-cancelable';
+103
View File
@@ -0,0 +1,103 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = require("@sindresorhus/is");
class GotError extends Error {
constructor(message, error, options) {
super(message);
Error.captureStackTrace(this, this.constructor);
this.name = 'GotError';
if (!is_1.default.undefined(error.code)) {
this.code = error.code;
}
Object.defineProperty(this, 'options', {
// This fails because of TS 3.7.2 useDefineForClassFields
// Ref: https://github.com/microsoft/TypeScript/issues/34972
enumerable: false,
value: options
});
// Recover the original stacktrace
if (!is_1.default.undefined(error.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.GotError = GotError;
class CacheError extends GotError {
constructor(error, options) {
super(error.message, error, options);
this.name = 'CacheError';
}
}
exports.CacheError = CacheError;
class RequestError extends GotError {
constructor(error, options) {
super(error.message, error, options);
this.name = 'RequestError';
}
}
exports.RequestError = RequestError;
class ReadError extends GotError {
constructor(error, options) {
super(error.message, error, options);
this.name = 'ReadError';
}
}
exports.ReadError = ReadError;
class ParseError extends GotError {
constructor(error, response, options) {
super(`${error.message} in "${options.url.toString()}"`, error, options);
this.name = 'ParseError';
Object.defineProperty(this, 'response', {
enumerable: false,
value: response
});
}
}
exports.ParseError = ParseError;
class HTTPError extends GotError {
constructor(response, options) {
super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, options);
this.name = 'HTTPError';
Object.defineProperty(this, 'response', {
enumerable: false,
value: response
});
}
}
exports.HTTPError = HTTPError;
class MaxRedirectsError extends GotError {
constructor(response, maxRedirects, options) {
super(`Redirected ${maxRedirects} times. Aborting.`, {}, options);
this.name = 'MaxRedirectsError';
Object.defineProperty(this, 'response', {
enumerable: false,
value: response
});
}
}
exports.MaxRedirectsError = MaxRedirectsError;
class UnsupportedProtocolError extends GotError {
constructor(options) {
super(`Unsupported protocol "${options.url.protocol}"`, {}, options);
this.name = 'UnsupportedProtocolError';
}
}
exports.UnsupportedProtocolError = UnsupportedProtocolError;
class TimeoutError extends GotError {
constructor(error, timings, options) {
super(error.message, error, options);
this.name = 'TimeoutError';
this.event = error.event;
this.timings = timings;
}
}
exports.TimeoutError = TimeoutError;
var p_cancelable_1 = require("p-cancelable");
exports.CancelError = p_cancelable_1.CancelError;
+6
View File
@@ -0,0 +1,6 @@
/// <reference types="node" />
import EventEmitter = require('events');
import { IncomingMessage } from 'http';
import { NormalizedOptions } from './types';
declare const _default: (response: IncomingMessage, options: NormalizedOptions, emitter: EventEmitter) => Promise<void>;
export default _default;
+25
View File
@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const decompressResponse = require("decompress-response");
const mimicResponse = require("mimic-response");
const stream = require("stream");
const util_1 = require("util");
const progress_1 = require("./progress");
const pipeline = util_1.promisify(stream.pipeline);
exports.default = async (response, options, emitter) => {
var _a;
const downloadBodySize = Number(response.headers['content-length']) || undefined;
const progressStream = progress_1.createProgressStream('downloadProgress', emitter, downloadBodySize);
mimicResponse(response, progressStream);
const newResponse = (options.decompress &&
options.method !== 'HEAD' ? decompressResponse(progressStream) : progressStream);
if (!options.decompress && ['gzip', 'deflate', 'br'].includes((_a = newResponse.headers['content-encoding'], (_a !== null && _a !== void 0 ? _a : '')))) {
options.responseType = 'buffer';
}
emitter.emit('response', newResponse);
return pipeline(response, progressStream).catch(error => {
if (error.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
throw error;
}
});
};
+7
View File
@@ -0,0 +1,7 @@
declare const got: import("./create").Got;
export default got;
export * from './types';
export { Got, GotStream, ReturnStream, GotRequestMethod, GotReturn } from './create';
export { ProxyStream as ResponseStream } from './as-stream';
export { GotError, CacheError, RequestError, ReadError, ParseError, HTTPError, MaxRedirectsError, UnsupportedProtocolError, TimeoutError, CancelError } from './errors';
export { InitHook, BeforeRequestHook, BeforeRedirectHook, BeforeRetryHook, BeforeErrorHook, AfterResponseHook, HookType, Hooks, HookEvent } from './known-hook-events';
+129
View File
@@ -0,0 +1,129 @@
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
const url_1 = require("url");
const create_1 = require("./create");
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: []
},
decompress: true,
throwHttpErrors: true,
followRedirect: true,
isStream: false,
cache: false,
dnsCache: false,
useElectronNet: false,
responseType: 'text',
resolveBodyOnly: false,
maxRedirects: 10,
prefixUrl: '',
methodRewriting: true,
allowGetBody: false,
ignoreInvalidCookies: false,
context: {},
_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
}
},
handlers: [create_1.defaultHandler],
mutableDefaults: false
};
const got = create_1.default(defaults);
exports.default = got;
// For CommonJS default export support
module.exports = got;
module.exports.default = got;
// Export types
__export(require("./types"));
var as_stream_1 = require("./as-stream");
exports.ResponseStream = as_stream_1.ProxyStream;
var errors_1 = require("./errors");
exports.GotError = errors_1.GotError;
exports.CacheError = errors_1.CacheError;
exports.RequestError = errors_1.RequestError;
exports.ReadError = errors_1.ReadError;
exports.ParseError = errors_1.ParseError;
exports.HTTPError = errors_1.HTTPError;
exports.MaxRedirectsError = errors_1.MaxRedirectsError;
exports.UnsupportedProtocolError = errors_1.UnsupportedProtocolError;
exports.TimeoutError = errors_1.TimeoutError;
exports.CancelError = errors_1.CancelError;
+88
View File
@@ -0,0 +1,88 @@
import { CancelableRequest, GeneralError, NormalizedOptions, Options, Response } from './types';
/**
Called with plain request options, right before their normalization. This is especially useful in conjunction with `got.extend()` when the input needs custom handling.
**Note:** This hook must be synchronous.
@see [Request migration guide](https://github.com/sindresorhus/got/blob/master/migration-guides.md#breaking-changes) for an example.
*/
export declare type InitHook = (options: Options) => void;
/**
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) when you want to create an API client that, for example, uses HMAC-signing.
@see [AWS section](https://github.com/sindresorhus/got#aws) for an example.
*/
export declare type BeforeRequestHook = (options: NormalizedOptions) => void | Promise<void>;
/**
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.
*/
export declare type BeforeRedirectHook = (options: NormalizedOptions, response: Response) => void | Promise<void>;
/**
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.
*/
export declare type BeforeRetryHook = (options: NormalizedOptions, error?: GeneralError, retryCount?: number) => void | Promise<void>;
/**
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.
*/
export declare type BeforeErrorHook = <ErrorLike extends GeneralError>(error: ErrorLike) => GeneralError | Promise<GeneralError>;
/**
Called with [response object](https://github.com/sindresorhus/got#response) and a retry function.
Each function should return the response. This is especially useful when you want to refresh an access token.
*/
export declare type AfterResponseHook = (response: Response, retryWithMergedOptions: (options: Options) => CancelableRequest<Response>) => Response | CancelableRequest<Response> | Promise<Response | CancelableRequest<Response>>;
export declare type HookType = BeforeErrorHook | InitHook | BeforeRequestHook | BeforeRedirectHook | BeforeRetryHook | AfterResponseHook;
/**
Hooks allow modifications during the request lifecycle. Hook functions may be async and are run serially.
*/
export interface Hooks {
/**
Called with plain request options, right before their normalization. This is especially useful in conjunction with `got.extend()` when the input needs custom handling.
**Note:** This hook must be synchronous.
@see [Request migration guide](https://github.com/sindresorhus/got/blob/master/migration-guides.md#breaking-changes) for an example.
@default []
*/
init?: InitHook[];
/**
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) when you want to create an API client that, for example, uses HMAC-signing.
@see [AWS section](https://github.com/sindresorhus/got#aws) for an example.
@default []
*/
beforeRequest?: BeforeRequestHook[];
/**
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.
@default []
*/
beforeRedirect?: BeforeRedirectHook[];
/**
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.
@default []
*/
beforeRetry?: BeforeRetryHook[];
/**
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 []
*/
beforeError?: BeforeErrorHook[];
/**
Called with [response object](https://github.com/sindresorhus/got#response) and a retry function.
Each function should return the response. This is especially useful when you want to refresh an access token.
@default []
*/
afterResponse?: AfterResponseHook[];
}
export declare type HookEvent = keyof Hooks;
declare const knownHookEvents: readonly HookEvent[];
export default knownHookEvents;
+11
View File
@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const knownHookEvents = [
'beforeError',
'init',
'beforeRequest',
'beforeRedirect',
'beforeRetry',
'afterResponse'
];
exports.default = knownHookEvents;
+19
View File
@@ -0,0 +1,19 @@
/// <reference types="node" />
import http = require('http');
import https = require('https');
import stream = require('stream');
import { Merge } from 'type-fest';
import { Defaults, NormalizedOptions, RequestFunction, URLOrOptions, requestSymbol } from './types';
export declare const preNormalizeArguments: (options: Merge<https.RequestOptions, Merge<import("./types").GotOptions, import("./utils/options-to-url").URLOptions>>, defaults?: NormalizedOptions | undefined) => NormalizedOptions;
export declare const mergeOptions: (...sources: Merge<https.RequestOptions, Merge<import("./types").GotOptions, import("./utils/options-to-url").URLOptions>>[]) => NormalizedOptions;
export declare const normalizeArguments: (url: URLOrOptions, options?: Merge<https.RequestOptions, Merge<import("./types").GotOptions, import("./utils/options-to-url").URLOptions>> | undefined, defaults?: Defaults | undefined) => NormalizedOptions;
export declare type NormalizedRequestArguments = Merge<https.RequestOptions, {
body?: stream.Readable;
[requestSymbol]: RequestFunction;
url: Pick<NormalizedOptions, 'url'>;
}>;
export declare const normalizeRequestArguments: (options: NormalizedOptions) => Promise<Merge<https.RequestOptions, {
body?: stream.Readable | undefined;
url: Pick<NormalizedOptions, "url">;
[requestSymbol]: typeof http.request;
}>>;
+441
View File
@@ -0,0 +1,441 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const url_1 = require("url");
const util_1 = require("util");
const CacheableRequest = require("cacheable-request");
const http = require("http");
const https = require("https");
const lowercaseKeys = require("lowercase-keys");
const toReadableStream = require("to-readable-stream");
const is_1 = require("@sindresorhus/is");
const cacheable_lookup_1 = require("cacheable-lookup");
const errors_1 = require("./errors");
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 merge_1 = require("./utils/merge");
const options_to_url_1 = require("./utils/options-to-url");
const supports_brotli_1 = require("./utils/supports-brotli");
const types_1 = require("./types");
const nonEnumerableProperties = [
'context',
'body',
'json',
'form'
];
const isAgentByProtocol = (agent) => is_1.default.object(agent);
// TODO: `preNormalizeArguments` should merge `options` & `defaults`
exports.preNormalizeArguments = (options, defaults) => {
var _a, _b, _c, _d, _e, _f;
// `options.headers`
if (is_1.default.undefined(options.headers)) {
options.headers = {};
}
else {
options.headers = lowercaseKeys(options.headers);
}
for (const [key, value] of Object.entries(options.headers)) {
if (is_1.default.null_(value)) {
throw new TypeError(`Use \`undefined\` instead of \`null\` to delete the \`${key}\` header`);
}
}
// `options.prefixUrl`
if (is_1.default.urlInstance(options.prefixUrl) || is_1.default.string(options.prefixUrl)) {
options.prefixUrl = options.prefixUrl.toString();
if (options.prefixUrl.length !== 0 && !options.prefixUrl.endsWith('/')) {
options.prefixUrl += '/';
}
}
else {
options.prefixUrl = defaults ? defaults.prefixUrl : '';
}
// `options.hooks`
if (is_1.default.undefined(options.hooks)) {
options.hooks = {};
}
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])}`);
}
}
else {
options.hooks[event] = [];
}
}
}
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) {
if (!(Reflect.has(options.hooks, event) && is_1.default.undefined(options.hooks[event]))) {
// @ts-ignore Union type array is not assignable to union array type
options.hooks[event] = [
...defaults.hooks[event],
...options.hooks[event]
];
}
}
}
// `options.timeout`
if (is_1.default.number(options.timeout)) {
options.timeout = { request: options.timeout };
}
else if (!is_1.default.object(options.timeout)) {
options.timeout = {};
}
// `options.retry`
const { retry } = options;
if (defaults) {
options.retry = { ...defaults.retry };
}
else {
options.retry = {
calculateDelay: retryObject => retryObject.computedValue,
limit: 0,
methods: [],
statusCodes: [],
errorCodes: [],
maxRetryAfter: undefined
};
}
if (is_1.default.object(retry)) {
options.retry = {
...options.retry,
...retry
};
}
else if (is_1.default.number(retry)) {
options.retry.limit = retry;
}
if (options.retry.maxRetryAfter === undefined) {
options.retry.maxRetryAfter = Math.min(...[options.timeout.request, options.timeout.connect].filter((n) => !is_1.default.nullOrUndefined(n)));
}
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 });
}
// `options.method`
if (is_1.default.string(options.method)) {
options.method = options.method.toUpperCase();
}
else {
options.method = (_b = (_a = defaults) === null || _a === void 0 ? void 0 : _a.method, (_b !== null && _b !== void 0 ? _b : 'GET'));
}
// Better memory management, so we don't have to generate a new object every time
if (options.cache) {
options.cacheableRequest = new CacheableRequest(
// @ts-ignore Cannot properly type a function with multiple definitions yet
(requestOptions, handler) => requestOptions[types_1.requestSymbol](requestOptions, handler), options.cache);
}
// `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)) {
// @ts-ignore TS is dumb - it says `setCookie` is `never`.
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 };
}
// `options.encoding`
if (is_1.default.null_(options.encoding)) {
throw new TypeError('To get a Buffer, set `options.responseType` to `buffer` instead');
}
// `options.maxRedirects`
if (!Reflect.has(options, 'maxRedirects') && !(defaults && Reflect.has(defaults, 'maxRedirects'))) {
options.maxRedirects = 0;
}
// Merge defaults
if (defaults) {
options = merge_1.default({}, defaults, options);
}
// `options._pagination`
if (is_1.default.object(options._pagination)) {
const { _pagination: pagination } = options;
if (!is_1.default.function_(pagination.transform)) {
throw new TypeError('`options._pagination.transform` must be implemented');
}
if (!is_1.default.function_(pagination.shouldContinue)) {
throw new TypeError('`options._pagination.shouldContinue` must be implemented');
}
if (!is_1.default.function_(pagination.filter)) {
throw new TypeError('`options._pagination.filter` must be implemented');
}
if (!is_1.default.function_(pagination.paginate)) {
throw new TypeError('`options._pagination.paginate` must be implemented');
}
}
// Other values
options.decompress = Boolean(options.decompress);
options.isStream = Boolean(options.isStream);
options.throwHttpErrors = Boolean(options.throwHttpErrors);
options.ignoreInvalidCookies = Boolean(options.ignoreInvalidCookies);
options.cache = (_c = options.cache, (_c !== null && _c !== void 0 ? _c : false));
options.responseType = (_d = options.responseType, (_d !== null && _d !== void 0 ? _d : 'text'));
options.resolveBodyOnly = Boolean(options.resolveBodyOnly);
options.followRedirect = Boolean(options.followRedirect);
options.dnsCache = (_e = options.dnsCache, (_e !== null && _e !== void 0 ? _e : false));
options.useElectronNet = Boolean(options.useElectronNet);
options.methodRewriting = Boolean(options.methodRewriting);
options.allowGetBody = Boolean(options.allowGetBody);
options.context = (_f = options.context, (_f !== null && _f !== void 0 ? _f : {}));
return options;
};
exports.mergeOptions = (...sources) => {
let mergedOptions = exports.preNormalizeArguments({});
// Non enumerable properties shall not be merged
const properties = {};
for (const source of sources) {
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]
};
}
}
Object.defineProperties(mergedOptions, properties);
return mergedOptions;
};
exports.normalizeArguments = (url, options, defaults) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
// Merge options
if (typeof url === 'undefined') {
throw new TypeError('Missing `url` argument');
}
const runInitHooks = (hooks, options) => {
if (hooks && options) {
for (const hook of hooks) {
const result = hook(options);
if (is_1.default.promise(result)) {
throw new TypeError('The `init` hook must be a synchronous function');
}
}
}
};
const hasUrl = is_1.default.urlInstance(url) || is_1.default.string(url);
if (hasUrl) {
if (options) {
if (Reflect.has(options, 'url')) {
throw new TypeError('The `url` option cannot be used if the input is a valid URL.');
}
}
else {
options = {};
}
// @ts-ignore URL is not URL
options.url = url;
runInitHooks((_a = defaults) === null || _a === void 0 ? void 0 : _a.options.hooks.init, options);
runInitHooks((_b = options.hooks) === null || _b === void 0 ? void 0 : _b.init, options);
}
else if (Reflect.has(url, 'resolve')) {
throw new Error('The legacy `url.Url` is deprecated. Use `URL` instead.');
}
else {
runInitHooks((_c = defaults) === null || _c === void 0 ? void 0 : _c.options.hooks.init, url);
runInitHooks((_d = url.hooks) === null || _d === void 0 ? void 0 : _d.init, url);
if (options) {
runInitHooks((_e = defaults) === null || _e === void 0 ? void 0 : _e.options.hooks.init, options);
runInitHooks((_f = options.hooks) === null || _f === void 0 ? void 0 : _f.init, options);
}
}
if (hasUrl) {
options = exports.mergeOptions((_h = (_g = defaults) === null || _g === void 0 ? void 0 : _g.options, (_h !== null && _h !== void 0 ? _h : {})), (options !== null && options !== void 0 ? options : {}));
}
else {
options = exports.mergeOptions((_k = (_j = defaults) === null || _j === void 0 ? void 0 : _j.options, (_k !== null && _k !== void 0 ? _k : {})), url, (options !== null && options !== void 0 ? options : {}));
}
// 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];
}
// @ts-ignore URL is not URL
options.url = options_to_url_1.default({
origin: options.url,
...options
});
}
else if (!is_1.default.urlInstance(options.url)) {
// @ts-ignore URL is not 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(normalizedOptions, 'prefixUrl', {
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_1.URL(value + normalizedOptions.url.href.slice(prefixUrl.length));
prefixUrl = value;
},
get: () => prefixUrl
});
// Make it possible to remove default headers
for (const [key, value] of Object.entries(normalizedOptions.headers)) {
if (is_1.default.undefined(value)) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete normalizedOptions.headers[key];
}
}
return normalizedOptions;
};
const withoutBody = new Set(['HEAD']);
const withoutBodyUnlessSpecified = 'GET';
exports.normalizeRequestArguments = async (options) => {
var _a, _b, _c;
options = exports.mergeOptions(options);
// Serialize body
const { headers } = options;
const hasNoContentType = is_1.default.undefined(headers['content-type']);
{
// TODO: these checks should be moved to `preNormalizeArguments`
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 (!options.allowGetBody && (isBody || isForm || isJson) && withoutBodyUnlessSpecified === 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 &&
!is_1.default.nodeStream(options.body) &&
!is_1.default.string(options.body) &&
!is_1.default.buffer(options.body) &&
!(is_1.default.object(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');
}
}
if (options.body) {
// Special case for https://github.com/form-data/form-data
if (is_1.default.object(options.body) && is_form_data_1.default(options.body) && hasNoContentType) {
headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`;
}
}
else if (options.form) {
if (hasNoContentType) {
headers['content-type'] = 'application/x-www-form-urlencoded';
}
options.body = (new url_1.URLSearchParams(options.form)).toString();
}
else if (options.json) {
if (hasNoContentType) {
headers['content-type'] = 'application/json';
}
options.body = JSON.stringify(options.json);
}
const uploadBodySize = await get_body_size_1.default(options);
if (!is_1.default.nodeStream(options.body)) {
options.body = toReadableStream(options.body);
}
// 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 ((options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH' || options.method === 'DELETE' || (options.allowGetBody && options.method === 'GET')) &&
!is_1.default.undefined(uploadBodySize)) {
// @ts-ignore We assign if it is undefined, so this IS correct
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[types_1.requestSymbol] = options.request;
delete options.request;
}
else {
options[types_1.requestSymbol] = 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;
options = {
...options,
socketPath,
path,
host: ''
};
}
}
if (isAgentByProtocol(options.agent)) {
options.agent = (_b = options.agent[options.url.protocol.slice(0, -1)], (_b !== null && _b !== void 0 ? _b : options.agent));
}
if (options.dnsCache) {
options.lookup = options.dnsCache.lookup;
}
/* 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 = util_1.deprecate((_c = electron.net.request, (_c !== null && _c !== void 0 ? _c : electron.remote.net.request)), 'Electron support has been deprecated and will be removed in Got 11.\n' +
'See https://github.com/sindresorhus/got/issues/899 for further information.', 'GOT_ELECTRON');
}
// 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;
};
+4
View File
@@ -0,0 +1,4 @@
/// <reference types="node" />
import EventEmitter = require('events');
import { Transform as TransformStream } from 'stream';
export declare function createProgressStream(name: 'downloadProgress' | 'uploadProgress', emitter: EventEmitter, totalBytes?: number | string): TransformStream;
+40
View File
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const stream_1 = require("stream");
const is_1 = require("@sindresorhus/is");
function createProgressStream(name, emitter, totalBytes) {
let transformedBytes = 0;
if (is_1.default.string(totalBytes)) {
totalBytes = Number(totalBytes);
}
const progressStream = new stream_1.Transform({
transform(chunk, _encoding, callback) {
transformedBytes += chunk.length;
const percent = totalBytes ? transformedBytes / totalBytes : 0;
// Let `flush()` be responsible for emitting the last event
if (percent < 1) {
emitter.emit(name, {
percent,
transferred: transformedBytes,
total: totalBytes
});
}
callback(undefined, chunk);
},
flush(callback) {
emitter.emit(name, {
percent: 1,
transferred: transformedBytes,
total: totalBytes
});
callback();
}
});
emitter.emit(name, {
percent: 0,
transferred: 0,
total: totalBytes
});
return progressStream;
}
exports.createProgressStream = createProgressStream;
+12
View File
@@ -0,0 +1,12 @@
/// <reference types="node" />
import EventEmitter = require('events');
import { ProxyStream } from './as-stream';
import { RequestError, TimeoutError } from './errors';
import { NormalizedOptions } from './types';
export interface RequestAsEventEmitter extends EventEmitter {
retry: (error: TimeoutError | RequestError) => boolean;
abort: () => void;
}
declare const _default: (options: NormalizedOptions) => RequestAsEventEmitter;
export default _default;
export declare const proxyEvents: (proxy: EventEmitter | ProxyStream<unknown>, emitter: RequestAsEventEmitter) => void;
+284
View File
@@ -0,0 +1,284 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const CacheableRequest = require("cacheable-request");
const EventEmitter = require("events");
const http = require("http");
const stream = require("stream");
const url_1 = require("url");
const util_1 = require("util");
const is_1 = require("@sindresorhus/is");
const http_timer_1 = require("@szmarczak/http-timer");
const calculate_retry_delay_1 = require("./calculate-retry-delay");
const errors_1 = require("./errors");
const get_response_1 = require("./get-response");
const normalize_arguments_1 = require("./normalize-arguments");
const progress_1 = require("./progress");
const timed_out_1 = require("./utils/timed-out");
const types_1 = require("./types");
const url_to_options_1 = require("./utils/url-to-options");
const pEvent = require("p-event");
const setImmediateAsync = async () => new Promise(resolve => setImmediate(resolve));
const pipeline = util_1.promisify(stream.pipeline);
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;
// `request.aborted` is a boolean since v11.0.0: https://github.com/nodejs/node/commit/4b00c4fafaa2ae8c41c1f78823c0feb810ae4723#diff-e3bc37430eb078ccbafe3aa3b570c91a
const isAborted = () => typeof currentRequest.aborted === 'number' || currentRequest.aborted;
const emitError = async (error) => {
try {
for (const hook of options.hooks.beforeError) {
// eslint-disable-next-line no-await-in-loop
error = await hook(error);
}
emitter.emit('error', error);
}
catch (error_) {
emitter.emit('error', error_);
}
};
const get = async () => {
let httpOptions = await normalize_arguments_1.normalizeRequestArguments(options);
const handleResponse = async (response) => {
var _a;
try {
/* istanbul ignore next: fixes https://github.com/electron/electron/blob/cbb460d47628a7a146adf4419ed48550a98b2923/lib/browser/api/net.js#L59-L65 */
if (options.useElectronNet) {
response = new Proxy(response, {
get: (target, name) => {
if (name === 'trailers' || name === 'rawTrailers') {
return [];
}
const value = target[name];
return is_1.default.function_(value) ? value.bind(target) : value;
}
});
}
const typedResponse = response;
const { statusCode } = typedResponse;
typedResponse.statusMessage = is_1.default.nonEmptyString(typedResponse.statusMessage) ? typedResponse.statusMessage : http.STATUS_CODES[statusCode];
typedResponse.url = options.url.toString();
typedResponse.requestUrl = requestUrl;
typedResponse.retryCount = retryCount;
typedResponse.redirectUrls = redirects;
typedResponse.request = { options };
typedResponse.isFromCache = (_a = typedResponse.fromCache, (_a !== null && _a !== void 0 ? _a : false));
delete typedResponse.fromCache;
if (!typedResponse.isFromCache) {
typedResponse.ip = response.socket.remoteAddress;
}
const rawCookies = typedResponse.headers['set-cookie'];
if (Reflect.has(options, 'cookieJar') && rawCookies) {
let promises = rawCookies.map(async (rawCookie) => options.cookieJar.setCookie(rawCookie, typedResponse.url));
if (options.ignoreInvalidCookies) {
promises = promises.map(async (p) => p.catch(() => { }));
}
await Promise.all(promises);
}
if (options.followRedirect && Reflect.has(typedResponse.headers, 'location') && redirectCodes.has(statusCode)) {
typedResponse.resume(); // We're being redirected, we don't care about the response.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare
if (statusCode === 303 || options.methodRewriting === false) {
if (options.method !== 'GET' && options.method !== 'HEAD') {
// 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 (Reflect.has(options, 'body')) {
delete options.body;
}
if (Reflect.has(options, 'json')) {
delete options.json;
}
if (Reflect.has(options, 'form')) {
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_1.URL(redirectBuffer, options.url);
// Redirecting to a different site, clear cookies.
if (redirectUrl.hostname !== options.url.hostname && Reflect.has(options.headers, 'cookie')) {
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;
}
await get_response_1.default(typedResponse, options, emitter);
}
catch (error) {
emitError(error);
}
};
const handleRequest = async (request) => {
let isPiped = false;
let isFinished = false;
// `request.finished` doesn't indicate whether this has been emitted or not
request.once('finish', () => {
isFinished = true;
});
currentRequest = request;
const onError = (error) => {
if (error instanceof timed_out_1.TimeoutError) {
error = new errors_1.TimeoutError(error, request.timings, options);
}
else {
error = new errors_1.RequestError(error, options);
}
if (!emitter.retry(error)) {
emitError(error);
}
};
request.on('error', error => {
if (isPiped) {
// Check if it's caught by `stream.pipeline(...)`
if (!isFinished) {
return;
}
// We need to let `TimedOutTimeoutError` through, because `stream.pipeline(…)` aborts the request automatically.
if (isAborted() && !(error instanceof timed_out_1.TimeoutError)) {
return;
}
}
onError(error);
});
try {
http_timer_1.default(request);
timed_out_1.default(request, options.timeout, options.url);
emitter.emit('request', request);
const uploadStream = progress_1.createProgressStream('uploadProgress', emitter, httpOptions.headers['content-length']);
isPiped = true;
await pipeline(httpOptions.body, uploadStream, request);
request.emit('upload-complete');
}
catch (error) {
if (isAborted() && error.message === 'Premature close') {
// The request was aborted on purpose
return;
}
onError(error);
}
};
if (options.cache) {
// `cacheable-request` doesn't support Node 10 API, fallback.
httpOptions = {
...httpOptions,
...url_to_options_1.default(options.url)
};
// @ts-ignore `cacheable-request` has got invalid types
const cacheRequest = options.cacheableRequest(httpOptions, handleResponse);
cacheRequest.once('error', (error) => {
if (error instanceof CacheableRequest.RequestError) {
emitError(new errors_1.RequestError(error, options));
}
else {
emitError(new errors_1.CacheError(error, options));
}
});
cacheRequest.once('request', handleRequest);
}
else {
// Catches errors thrown by calling `requestFn(…)`
try {
handleRequest(httpOptions[types_1.requestSymbol](options.url, httpOptions, handleResponse));
}
catch (error) {
emitError(new errors_1.RequestError(error, options));
}
}
};
emitter.retry = error => {
let backoff;
retryCount++;
try {
backoff = options.retry.calculateDelay({
attemptCount: retryCount,
retryOptions: options.retry,
error,
computedValue: calculate_retry_delay_1.default({
attemptCount: retryCount,
retryOptions: options.retry,
error,
computedValue: 0
})
});
}
catch (error_) {
emitError(error_);
return false;
}
if (backoff) {
const retry = async (options) => {
try {
for (const hook of options.hooks.beforeRetry) {
// eslint-disable-next-line no-await-in-loop
await hook(options, error, retryCount);
}
await get();
}
catch (error_) {
emitError(error_);
}
};
setTimeout(retry, backoff, { ...options, forceRefresh: true });
return true;
}
return false;
};
emitter.abort = () => {
emitter.prependListener('request', (request) => {
request.abort();
});
if (currentRequest) {
currentRequest.abort();
}
};
(async () => {
try {
if (options.body instanceof fs_1.ReadStream) {
await pEvent(options.body, 'open');
}
// Promises are executed immediately.
// If there were no `setImmediate` here,
// `promise.json()` would have no effect
// as the request would be sent already.
await setImmediateAsync();
for (const hook of options.hooks.beforeRequest) {
// eslint-disable-next-line no-await-in-loop
await hook(options);
}
await get();
}
catch (error) {
emitError(error);
}
})();
return emitter;
};
exports.proxyEvents = (proxy, emitter) => {
const events = [
'request',
'redirect',
'uploadProgress',
'downloadProgress'
];
for (const event of events) {
emitter.on(event, (...args) => {
proxy.emit(event, ...args);
});
}
};
+210
View File
@@ -0,0 +1,210 @@
/// <reference types="node" />
import http = require('http');
import https = require('https');
import Keyv = require('keyv');
import CacheableRequest = require('cacheable-request');
import PCancelable = require('p-cancelable');
import ResponseLike = require('responselike');
import { URL } from 'url';
import { Readable as ReadableStream } from 'stream';
import { Timings, IncomingMessageWithTimings } from '@szmarczak/http-timer';
import CacheableLookup from 'cacheable-lookup';
import { Except, Merge } from 'type-fest';
import { GotReturn } from './create';
import { GotError, HTTPError, MaxRedirectsError, ParseError, TimeoutError, RequestError } from './errors';
import { Hooks } from './known-hook-events';
import { URLOptions } from './utils/options-to-url';
export declare type GeneralError = Error | GotError | HTTPError | MaxRedirectsError | ParseError;
export declare type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'TRACE' | 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete' | 'options' | 'trace';
export declare type ResponseType = 'json' | 'buffer' | 'text';
export interface Response<BodyType = unknown> extends IncomingMessageWithTimings {
body: BodyType;
statusCode: number;
/**
The remote IP address.
Note: Not available when the response is cached. This is hopefully a temporary limitation, see [lukechilds/cacheable-request#86](https://github.com/lukechilds/cacheable-request/issues/86).
*/
ip: string;
fromCache?: boolean;
isFromCache?: boolean;
req?: http.ClientRequest;
requestUrl: string;
retryCount: number;
timings: Timings;
redirectUrls: string[];
request: {
options: NormalizedOptions;
};
url: string;
}
export interface ResponseObject extends Partial<ResponseLike> {
socket: {
remoteAddress: string;
};
}
export interface RetryObject {
attemptCount: number;
retryOptions: Required<RetryOptions>;
error: TimeoutError | RequestError;
computedValue: number;
}
export declare type RetryFunction = (retryObject: RetryObject) => number;
export declare type HandlerFunction = <T extends GotReturn>(options: NormalizedOptions, next: (options: NormalizedOptions) => T) => T | Promise<T>;
export interface DefaultRetryOptions {
limit: number;
methods: Method[];
statusCodes: number[];
errorCodes: string[];
calculateDelay: RetryFunction;
maxRetryAfter?: number;
}
export interface RetryOptions extends Partial<DefaultRetryOptions> {
retries?: number;
}
export declare type RequestFunction = typeof http.request;
export interface AgentByProtocol {
http?: http.Agent;
https?: https.Agent;
}
export interface Delays {
lookup?: number;
connect?: number;
secureConnect?: number;
socket?: number;
response?: number;
send?: number;
request?: number;
}
export declare type Headers = Record<string, string | string[] | undefined>;
interface ToughCookieJar {
getCookieString(currentUrl: string, options: {
[key: string]: unknown;
}, cb: (err: Error | null, cookies: string) => void): void;
getCookieString(url: string, callback: (error: Error | null, cookieHeader: string) => void): void;
setCookie(cookieOrString: unknown, currentUrl: string, options: {
[key: string]: unknown;
}, cb: (err: Error | null, cookie: unknown) => void): void;
setCookie(rawCookie: string, url: string, callback: (error: Error | null, result: unknown) => void): void;
}
interface PromiseCookieJar {
getCookieString(url: string): Promise<string>;
setCookie(rawCookie: string, url: string): Promise<unknown>;
}
export declare const requestSymbol: unique symbol;
export declare type DefaultOptions = Merge<Required<Except<GotOptions, 'hooks' | 'retry' | 'timeout' | 'context' | '_pagination' | 'agent' | 'body' | 'cookieJar' | 'encoding' | 'form' | 'json' | 'lookup' | 'request' | 'url' | typeof requestSymbol>>, {
hooks: Required<Hooks>;
retry: DefaultRetryOptions;
timeout: Delays;
context: {
[key: string]: any;
};
_pagination?: PaginationOptions<unknown>['_pagination'];
}>;
export interface PaginationOptions<T> {
_pagination?: {
transform?: (response: Response) => Promise<T[]> | T[];
filter?: (item: T, allItems: T[], currentItems: T[]) => boolean;
paginate?: (response: Response, allItems: T[], currentItems: T[]) => Options | false;
shouldContinue?: (item: T, allItems: T[], currentItems: T[]) => boolean;
countLimit?: number;
};
}
export interface GotOptions extends PaginationOptions<unknown> {
[requestSymbol]?: RequestFunction;
url?: URL | string;
body?: string | Buffer | ReadableStream;
hooks?: Hooks;
decompress?: boolean;
isStream?: boolean;
encoding?: BufferEncoding;
method?: Method;
retry?: RetryOptions | number;
throwHttpErrors?: boolean;
cookieJar?: ToughCookieJar | PromiseCookieJar;
ignoreInvalidCookies?: boolean;
request?: RequestFunction;
agent?: http.Agent | https.Agent | boolean | AgentByProtocol;
cache?: string | CacheableRequest.StorageAdapter | false;
headers?: Headers;
responseType?: ResponseType;
resolveBodyOnly?: boolean;
followRedirect?: boolean;
prefixUrl?: URL | string;
timeout?: number | Delays;
dnsCache?: CacheableLookup | Map<string, string> | Keyv | false;
useElectronNet?: boolean;
form?: {
[key: string]: any;
};
json?: {
[key: string]: any;
};
context?: {
[key: string]: any;
};
maxRedirects?: number;
lookup?: CacheableLookup['lookup'];
allowGetBody?: boolean;
methodRewriting?: boolean;
}
export declare type Options = Merge<https.RequestOptions, Merge<GotOptions, URLOptions>>;
export interface NormalizedOptions extends Options {
headers: Headers;
hooks: Required<Hooks>;
timeout: Delays;
dnsCache: CacheableLookup | false;
lookup?: CacheableLookup['lookup'];
retry: Required<RetryOptions>;
prefixUrl: string;
method: Method;
url: URL;
cacheableRequest?: (options: string | URL | http.RequestOptions, callback?: (response: http.ServerResponse | ResponseLike) => void) => CacheableRequest.Emitter;
cookieJar?: PromiseCookieJar;
maxRedirects: number;
pagination?: Required<PaginationOptions<unknown>['_pagination']>;
[requestSymbol]: RequestFunction;
decompress: boolean;
isStream: boolean;
throwHttpErrors: boolean;
ignoreInvalidCookies: boolean;
cache: CacheableRequest.StorageAdapter | false;
responseType: ResponseType;
resolveBodyOnly: boolean;
followRedirect: boolean;
useElectronNet: boolean;
methodRewriting: boolean;
allowGetBody: boolean;
context: {
[key: string]: any;
};
path?: string;
}
export interface ExtendOptions extends Options {
handlers?: HandlerFunction[];
mutableDefaults?: boolean;
}
export interface Defaults {
options: DefaultOptions;
handlers: HandlerFunction[];
mutableDefaults: boolean;
_rawHandlers?: HandlerFunction[];
}
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 PCancelable<T>, GotEvents<CancelableRequest<T>> {
json<ReturnType>(): CancelableRequest<ReturnType>;
buffer(): CancelableRequest<Buffer>;
text(): CancelableRequest<string>;
}
export {};
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.requestSymbol = Symbol('request');
+1
View File
@@ -0,0 +1 @@
export default function deepFreeze<T extends object>(object: T): Readonly<T>;
+12
View File
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = require("@sindresorhus/is");
function deepFreeze(object) {
for (const value of Object.values(object)) {
if (is_1.default.plainObject(value) || is_1.default.array(value)) {
deepFreeze(value);
}
}
return Object.freeze(object);
}
exports.default = deepFreeze;
+3
View File
@@ -0,0 +1,3 @@
/// <reference types="node" />
declare const _default: (moduleObject: NodeModule, moduleId: string) => unknown;
export default _default;
+4
View File
@@ -0,0 +1,4 @@
"use strict";
/* istanbul ignore file: used for webpack */
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = (moduleObject, moduleId) => moduleObject.require(moduleId);
+8
View File
@@ -0,0 +1,8 @@
/// <reference types="node" />
import { ClientRequestArgs } from 'http';
interface Options {
body?: unknown;
headers: ClientRequestArgs['headers'];
}
declare const _default: (options: Options) => Promise<number | undefined>;
export default _default;
+30
View File
@@ -0,0 +1,30 @@
"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 (options) => {
const { body, headers } = options;
if (headers && 'content-length' in headers) {
return Number(headers['content-length']);
}
if (!body) {
return 0;
}
if (is_1.default.string(body)) {
return Buffer.byteLength(body);
}
if (is_1.default.buffer(body)) {
return body.length;
}
if (is_form_data_1.default(body)) {
return util_1.promisify(body.getLength.bind(body))();
}
if (body instanceof fs_1.ReadStream) {
const { size } = await statAsync(body.path);
return size;
}
return undefined;
};
+3
View File
@@ -0,0 +1,3 @@
import FormData = require('form-data');
declare const _default: (body: unknown) => body is FormData;
export default _default;
+4
View File
@@ -0,0 +1,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);
+6
View File
@@ -0,0 +1,6 @@
import { Merge } from 'type-fest';
export default function merge<Target extends {
[key: string]: any;
}, Source extends {
[key: string]: any;
}>(target: Target, ...sources: Source[]): Merge<Source, Target>;
+35
View File
@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const url_1 = require("url");
const is_1 = require("@sindresorhus/is");
function merge(target, ...sources) {
for (const source of sources) {
for (const [key, sourceValue] of Object.entries(source)) {
const targetValue = target[key];
if (is_1.default.urlInstance(targetValue) && is_1.default.string(sourceValue)) {
// @ts-ignore TS doesn't recognise Target accepts string keys
target[key] = new url_1.URL(sourceValue, targetValue);
}
else if (is_1.default.plainObject(sourceValue)) {
if (is_1.default.plainObject(targetValue)) {
// @ts-ignore TS doesn't recognise Target accepts string keys
target[key] = merge({}, targetValue, sourceValue);
}
else {
// @ts-ignore TS doesn't recognise Target accepts string keys
target[key] = merge({}, sourceValue);
}
}
else if (is_1.default.array(sourceValue)) {
// @ts-ignore TS doesn't recognise Target accepts string keys
target[key] = sourceValue.slice();
}
else {
// @ts-ignore TS doesn't recognise Target accepts string keys
target[key] = sourceValue;
}
}
}
return target;
}
exports.default = merge;
+19
View File
@@ -0,0 +1,19 @@
/// <reference types="node" />
import { URL, URLSearchParams } from 'url';
export interface URLOptions {
href?: string;
origin?: string;
protocol?: string;
username?: string;
password?: string;
host?: string;
hostname?: string;
port?: string | number;
pathname?: string;
search?: string;
searchParams?: Record<string, string | number | boolean | null> | URLSearchParams | string;
hash?: string;
path?: string;
}
declare const _default: (options: URLOptions) => URL;
export default _default;
+82
View File
@@ -0,0 +1,82 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const url_1 = require("url");
function validateSearchParams(searchParams) {
for (const value of Object.values(searchParams)) {
if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean' && value !== null) {
throw new TypeError(`The \`searchParams\` value '${String(value)}' must be a string, number, boolean or null`);
}
}
}
const keys = [
'protocol',
'username',
'password',
'host',
'hostname',
'port',
'pathname',
'search',
'hash'
];
exports.default = (options) => {
var _a, _b;
let origin;
if (options.path) {
if (options.pathname) {
throw new TypeError('Parameters `path` and `pathname` are mutually exclusive.');
}
if (options.search) {
throw new TypeError('Parameters `path` and `search` are mutually exclusive.');
}
if (options.searchParams) {
throw new TypeError('Parameters `path` and `searchParams` are mutually exclusive.');
}
}
if (Reflect.has(options, 'auth')) {
throw new TypeError('Parameter `auth` is deprecated. Use `username` / `password` instead.');
}
if (options.search && options.searchParams) {
throw new TypeError('Parameters `search` and `searchParams` are mutually exclusive.');
}
if (options.href) {
return new url_1.URL(options.href);
}
if (options.origin) {
origin = options.origin;
}
else {
if (!options.protocol) {
throw new TypeError('No URL protocol specified');
}
origin = `${options.protocol}//${_b = (_a = options.hostname, (_a !== null && _a !== void 0 ? _a : options.host)), (_b !== null && _b !== void 0 ? _b : '')}`;
}
const url = new url_1.URL(origin);
if (options.path) {
const searchIndex = options.path.indexOf('?');
if (searchIndex === -1) {
options.pathname = options.path;
}
else {
options.pathname = options.path.slice(0, searchIndex);
options.search = options.path.slice(searchIndex + 1);
}
}
if (Reflect.has(options, 'path')) {
delete options.path;
}
for (const key of keys) {
if (Reflect.has(options, key)) {
url[key] = options[key].toString();
}
}
if (options.searchParams) {
if (typeof options.searchParams !== 'string' && !(options.searchParams instanceof url_1.URLSearchParams)) {
validateSearchParams(options.searchParams);
}
(new url_1.URLSearchParams(options.searchParams)).forEach((value, key) => {
url.searchParams.append(key, value);
});
}
return url;
};
+2
View File
@@ -0,0 +1,2 @@
declare const _default: boolean;
export default _default;
+4
View File
@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const zlib = require("zlib");
exports.default = typeof zlib.createBrotliDecompress === 'function';
+29
View File
@@ -0,0 +1,29 @@
import { ClientRequest } from 'http';
declare const reentry: unique symbol;
interface TimedOutOptions {
host?: string;
hostname?: string;
protocol?: string;
}
export interface Delays {
lookup?: number;
connect?: number;
secureConnect?: number;
socket?: number;
response?: number;
send?: number;
request?: number;
}
export declare type ErrorCode = 'ETIMEDOUT' | 'ECONNRESET' | 'EADDRINUSE' | 'ECONNREFUSED' | 'EPIPE' | 'ENOTFOUND' | 'ENETUNREACH' | 'EAI_AGAIN';
export declare class TimeoutError extends Error {
event: string;
code: ErrorCode;
constructor(threshold: number, event: string);
}
declare const _default: (request: ClientRequest, delays: Delays, options: TimedOutOptions) => () => void;
export default _default;
declare module 'http' {
interface ClientRequest {
[reentry]: boolean;
}
}
+125
View File
@@ -0,0 +1,125 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const net = require("net");
const unhandle_1 = require("./unhandle");
const reentry = Symbol('reentry');
const noop = () => { };
class TimeoutError extends Error {
constructor(threshold, event) {
super(`Timeout awaiting '${event}' for ${threshold}ms`);
this.event = event;
this.name = 'TimeoutError';
this.code = 'ETIMEDOUT';
}
}
exports.TimeoutError = TimeoutError;
exports.default = (request, delays, options) => {
if (Reflect.has(request, reentry)) {
return noop;
}
request[reentry] = true;
const cancelers = [];
const { once, unhandleAll } = unhandle_1.default();
const addTimeout = (delay, callback, event) => {
var _a, _b;
const timeout = setTimeout(callback, delay, delay, event);
(_b = (_a = timeout).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
const cancel = () => {
clearTimeout(timeout);
};
cancelers.push(cancel);
return cancel;
};
const { host, hostname } = options;
const timeoutHandler = (delay, event) => {
if (request.socket) {
// @ts-ignore We do not want the `socket hang up` error
request.socket._hadError = true;
}
request.abort();
request.emit('error', new TimeoutError(delay, event));
};
const cancelTimeouts = () => {
for (const cancel of cancelers) {
cancel();
}
unhandleAll();
};
request.once('error', error => {
cancelTimeouts();
// Save original behavior
if (request.listenerCount('error') === 0) {
throw error;
}
});
request.once('abort', cancelTimeouts);
once(request, 'response', (response) => {
once(response, 'end', cancelTimeouts);
});
if (typeof delays.request !== 'undefined') {
addTimeout(delays.request, timeoutHandler, 'request');
}
if (typeof delays.socket !== 'undefined') {
const socketTimeoutHandler = () => {
timeoutHandler(delays.socket, 'socket');
};
request.setTimeout(delays.socket, socketTimeoutHandler);
// `request.setTimeout(0)` causes a memory leak.
// We can just remove the listener and forget about the timer - it's unreffed.
// See https://github.com/sindresorhus/got/issues/690
cancelers.push(() => {
request.removeListener('timeout', socketTimeoutHandler);
});
}
once(request, 'socket', (socket) => {
var _a;
// @ts-ignore Node typings doesn't have this property
const { socketPath } = request;
/* istanbul ignore next: hard to test */
if (socket.connecting) {
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');
once(socket, 'lookup', cancelTimeout);
}
if (typeof delays.connect !== 'undefined') {
const timeConnect = () => addTimeout(delays.connect, timeoutHandler, 'connect');
if (hasPath) {
once(socket, 'connect', timeConnect());
}
else {
once(socket, 'lookup', (error) => {
if (error === null) {
once(socket, 'connect', timeConnect());
}
});
}
}
if (typeof delays.secureConnect !== 'undefined' && options.protocol === 'https:') {
once(socket, 'connect', () => {
const cancelTimeout = addTimeout(delays.secureConnect, timeoutHandler, 'secureConnect');
once(socket, 'secureConnect', cancelTimeout);
});
}
}
if (typeof delays.send !== 'undefined') {
const timeRequest = () => addTimeout(delays.send, timeoutHandler, 'send');
/* istanbul ignore next: hard to test */
if (socket.connecting) {
once(socket, 'connect', () => {
once(request, 'upload-complete', timeRequest());
});
}
else {
once(request, 'upload-complete', timeRequest());
}
}
});
if (typeof delays.response !== 'undefined') {
once(request, 'upload-complete', () => {
const cancelTimeout = addTimeout(delays.response, timeoutHandler, 'response');
once(request, 'response', cancelTimeout);
});
}
return cancelTimeouts;
};
+11
View File
@@ -0,0 +1,11 @@
/// <reference types="node" />
import EventEmitter = require('events');
declare type Origin = EventEmitter;
declare type Event = string | symbol;
declare type Fn = (...args: any[]) => void;
interface Unhandler {
once: (origin: Origin, event: Event, fn: Fn) => void;
unhandleAll: () => void;
}
declare const _default: () => Unhandler;
export default _default;
+22
View File
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// When attaching listeners, it's very easy to forget about them.
// Especially if you do error handling and set timeouts.
// So instead of checking if it's proper to throw an error on every timeout ever,
// use this simple tool which will remove all listeners you have attached.
exports.default = () => {
const handlers = [];
return {
once(origin, event, fn) {
origin.once(event, fn);
handlers.push({ origin, event, fn });
},
unhandleAll() {
for (const handler of handlers) {
const { origin, event, fn } = handler;
origin.removeListener(event, fn);
}
handlers.length = 0;
}
};
};
+16
View File
@@ -0,0 +1,16 @@
/// <reference types="node" />
import { URL, UrlWithStringQuery } from 'url';
export interface LegacyUrlOptions {
protocol: string;
hostname: string;
host: string;
hash: string | null;
search: string | null;
pathname: string;
href: string;
path: string;
port?: number;
auth?: string;
}
declare const _default: (url: URL | UrlWithStringQuery) => LegacyUrlOptions;
export default _default;
+24
View File
@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = require("@sindresorhus/is");
exports.default = (url) => {
// Cast to URL
url = url;
const options = {
protocol: url.protocol,
hostname: is_1.default.string(url.hostname) && url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname,
host: url.host,
hash: url.hash,
search: url.search,
pathname: url.pathname,
href: url.href,
path: `${url.pathname || ''}${url.search || ''}`
};
if (is_1.default.string(url.port) && url.port.length !== 0) {
options.port = Number(url.port);
}
if (url.username || url.password) {
options.auth = `${url.username || ''}:${url.password || ''}`;
}
return options;
};