diff --git a/source/as-promise/core.ts b/source/as-promise/core.ts index 294e6e12b..744e93187 100644 --- a/source/as-promise/core.ts +++ b/source/as-promise/core.ts @@ -1,7 +1,7 @@ import {URL} from 'url'; import is, {assert} from '@sindresorhus/is'; import {Options, NormalizedOptions, Defaults} from './types'; -import {Request, knownHookEvents} from '../core'; +import Request, {knownHookEvents} from '../core'; if (!knownHookEvents.includes('beforeRetry' as any)) { knownHookEvents.push('beforeRetry' as any, 'afterResponse' as any); @@ -10,11 +10,12 @@ if (!knownHookEvents.includes('beforeRetry' as any)) { export default class PromisableRequest extends Request { ['constructor']: typeof PromisableRequest; - static normalizeArguments(url?: string | URL, nonNormalizedOptions?: Options, defaults?: Defaults) { + static normalizeArguments(url?: string | URL, nonNormalizedOptions?: Options, defaults?: Defaults): NormalizedOptions { const options = super.normalizeArguments(url, nonNormalizedOptions, defaults) as NormalizedOptions; if (!('responseType' in options)) { - options!.responseType = 'text'; + // @ts-ignore TypeScript bug - it says `options` is `never` + options.responseType = 'text'; } assert.any([is.boolean, is.undefined], options.resolveBodyOnly); @@ -74,7 +75,7 @@ export default class PromisableRequest extends Request { if (!is.function_(pagination.shouldContinue)) { throw new Error('`options._pagination.shouldContinue` must be implemented'); } - PromisableRequest + if (!is.function_(pagination.paginate)) { throw new Error('`options._pagination.paginate` must be implemented'); } diff --git a/source/as-promise/index.ts b/source/as-promise/index.ts index 606e4d4f5..e26d8c12c 100644 --- a/source/as-promise/index.ts +++ b/source/as-promise/index.ts @@ -14,7 +14,14 @@ import { import PromisableRequest from './core'; import proxyEvents from '../utils/proxy-events'; +export const knownBodyTypes = ['json', 'buffer', 'text']; + +// @ts-ignore The error is: Not all code paths return a value. const parseBody = (body: Buffer, responseType: NormalizedOptions['responseType'], encoding: NormalizedOptions['encoding']): unknown => { + if (responseType === 'text') { + return body.toString(encoding); + } + if (responseType === 'json') { return body.length === 0 ? '' : JSON.parse(body.toString()); } @@ -23,11 +30,9 @@ const parseBody = (body: Buffer, responseType: NormalizedOptions['responseType'] return Buffer.from(body); } - if (responseType === 'text') { - return body.toString(encoding); + if (!knownBodyTypes.includes(responseType)) { + throw new TypeError(`Unknown body type '${responseType as string}'`); } - - throw new TypeError(`Unknown body type '${responseType as string}'`); }; export default function asPromise(options: NormalizedOptions): CancelableRequest { @@ -36,7 +41,7 @@ export default function asPromise(options: NormalizedOptions): CancelableRequ const emitter = new EventEmitter(); const promise = new PCancelable((resolve, reject, onCancel) => { - const makeRequest = () => { + const makeRequest = (): void => { const request = new PromisableRequest(options.url, options); onCancel(request.destroy); diff --git a/source/core/index.ts b/source/core/index.ts index 0ba856905..b4afdf3e1 100644 --- a/source/core/index.ts +++ b/source/core/index.ts @@ -2,6 +2,7 @@ import {promisify} from 'util'; import {Duplex, Writable, Readable} from 'stream'; import {ReadStream} from 'fs'; import {URL} from 'url'; +import {Socket} from 'net'; import http = require('http'); import {ClientRequest, RequestOptions, IncomingMessage, ServerResponse, request as httpRequest} from 'http'; import https = require('https'); @@ -167,9 +168,9 @@ export interface Defaults { } export interface Progress { - percent: number; - transferred: number; - total?: number; + percent: number; + transferred: number; + total?: number; } export interface Response extends IncomingMessage { @@ -188,7 +189,8 @@ export interface RequestEvents { on(name: 'uploadProgress' | 'downloadProgress', listener: (progress: Progress) => void): T; } -function validateSearchParams (searchParams: Record): asserts searchParams is Record { +function validateSearchParams(searchParams: Record): asserts searchParams is Record { + // eslint-disable-next-line guard-for-in for (const key in searchParams) { const value = searchParams[key]; @@ -198,7 +200,7 @@ function validateSearchParams (searchParams: Record): asserts s } } -const cacheFn = (url: URL, options: RequestOptions) => new Promise((resolve, reject) => { +const cacheFn = async (url: URL, options: RequestOptions): Promise => new Promise((resolve, reject) => { // TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed Object.assign(options, urlToOptions(url)); @@ -221,8 +223,8 @@ const cacheFn = (url: URL, options: RequestOptions) => new Promise new Promise((resolve, reject) => { - const onError = (error: Error) => { +const waitForOpenFile = async (file: ReadStream): Promise => new Promise((resolve, reject) => { + const onError = (error: Error): void => { reject(error); }; @@ -258,8 +260,8 @@ export class RequestError extends Error { }); if (requestOrResponse instanceof IncomingMessage) { - this.response = requestOrResponse as Response; - requestOrResponse = (requestOrResponse as Response).request; + this.response = requestOrResponse; + requestOrResponse = requestOrResponse.request; } if (requestOrResponse instanceof Request) { @@ -304,7 +306,6 @@ export class CacheError extends RequestError { } } - export class UploadError extends RequestError { constructor(error: Error, options: NormalizedOptions, request: Request) { super(error.message, error, options, request); @@ -331,7 +332,7 @@ export class ReadError extends RequestError { } } -export class Request extends Duplex implements RequestEvents { +export default class Request extends Duplex implements RequestEvents { ['constructor']: typeof Request; declare [kRequest]: ClientRequest; @@ -348,7 +349,91 @@ export class Request extends Duplex implements RequestEvents { finalized: boolean; redirects: string[]; - static normalizeArguments(url?: string | URL, options?: Options, defaults?: Defaults) { + constructor(url: string | URL, options?: Options, defaults?: Defaults) { + super(); + + this[kDownloadedSize] = 0; + this[kUploadedSize] = 0; + this.finalized = false; + this[kServerResponsesPiped] = new Set(); + this.redirects = []; + + if (!options) { + options = {}; + } + + this.on('pipe', source => { + if (source instanceof IncomingMessage) { + this.options.headers = { + ...source.headers, + ...this.options.headers + }; + } + }); + + (async (nonNormalizedOptions: Options) => { + try { + if (nonNormalizedOptions.body instanceof ReadStream) { + await waitForOpenFile(nonNormalizedOptions.body); + } + + const initHooks = nonNormalizedOptions.hooks?.init; + if (initHooks) { + nonNormalizedOptions = {...nonNormalizedOptions, url}; + + for (const hook of initHooks) { + // eslint-disable-next-line no-await-in-loop + await hook(nonNormalizedOptions as Options & {url: string | URL}); + } + } + + if (kIsNormalizedAlready in nonNormalizedOptions) { + this.options = nonNormalizedOptions as NormalizedOptions; + } else { + // @ts-ignore Common TypeScript bug saying that `this.constructor` is not accessible + this.options = this.constructor.normalizeArguments(url, nonNormalizedOptions, defaults); + } + + const {options} = this; + + this.requestUrl = options.url.toString(); + + await this.finalizeBody(); + + // Set cookies + if (options.cookieJar) { + const cookieString: string = await options.cookieJar.getCookieString(options.url.toString()); + + if (cookieString !== '') { + options.headers.cookie = cookieString; + } + } + + if (options.encoding) { + this.setEncoding(options.encoding); + } + + await this.makeRequest(); + + if (options.body instanceof Readable) { + options.body.pipe(this); + options.body.once('error', (error: NodeJS.ErrnoException) => { + this._beforeError(new UploadError(error, options, this)); + }); + } else if (options.body) { + this._writeRequest(options.body, null as unknown as string, () => {}); + this.end(); + } + + this.finalized = true; + this.emit('finalized'); + } catch (error) { + this._beforeError(error); + } + })(options); + } + + static normalizeArguments(url?: string | URL, options?: Options, defaults?: Defaults): NormalizedOptions { if (is.object(url) && !is.urlInstance(url)) { options = {...defaults as NormalizedOptions, ...(url as Options)}; } else { @@ -363,19 +448,19 @@ export class Request extends Duplex implements RequestEvents { // `options.method` if (is.string(options.method)) { options.method = options.method.toUpperCase(); - } else if (!is.undefined(options.method)) { - throw new TypeError(`Parameter \`method\` must be a string, not ${is(options.method)}`); - } else { + } else if (is.undefined(options.method)) { options.method = 'GET'; + } else { + throw new TypeError(`Parameter \`method\` must be a string, not ${is(options.method)}`); } // `options.headers` if (is.undefined(options.headers)) { options.headers = {}; - } else if (!is.object(options.headers)) { - throw new TypeError(`Parameter \`headers\` must be an object, not ${is(options.headers)}`); - } else { + } else if (is.object(options.headers)) { options.headers = lowercaseKeys({...(defaults?.headers), ...options.headers}); + } else { + throw new TypeError(`Parameter \`headers\` must be an object, not ${is(options.headers)}`); } // `options.prefixUrl` & `options.url` @@ -400,11 +485,11 @@ export class Request extends Duplex implements RequestEvents { // `options.username` & `options.password` if (options.username || options.password) { if ('auth' in options) { - throw new TypeError(`Parameter \`auth\` is mutually exclusive with the \`username\` and \`password\` option`); + throw new TypeError('Parameter `auth` is mutually exclusive with the `username` and `password` option'); } - options.url!.username = options.username || ''; - options.url!.password = options.password || ''; + options.url!.username = options.username ?? ''; + options.url!.password = options.password ?? ''; } // `options.cookieJar` @@ -442,7 +527,7 @@ export class Request extends Duplex implements RequestEvents { // `normalizeArguments()` is also used to merge options const defaultsAsOptions = defaults as Options | undefined; - if (defaultsAsOptions && defaultsAsOptions['searchParams'] instanceof URLSearchParams) { + if (defaultsAsOptions && defaultsAsOptions.searchParams instanceof URLSearchParams) { defaultsAsOptions.searchParams.forEach((value, key) => { (options!.searchParams as URLSearchParams).append(key, value); }); @@ -484,10 +569,10 @@ export class Request extends Duplex implements RequestEvents { options.timeout = {request: options.timeout}; } else if (is.undefined(options.timeout)) { options.timeout = {}; - } else if (!is.object(options.timeout)) { - throw new TypeError(`Parameter \`timeout\` must be an object or a number, not ${is(options.timeout)}`); - } else { + } else if (is.object(options.timeout)) { options.timeout = {...options.timeout}; + } else { + throw new TypeError(`Parameter \`timeout\` must be an object or a number, not ${is(options.timeout)}`); } if (defaults) { @@ -500,10 +585,10 @@ export class Request extends Duplex implements RequestEvents { // `options.context` if (is.undefined(options.context)) { options.context = {}; - } else if (!is.object(options.context)) { - throw new TypeError(`Parameter \`context\` must be an object, not ${is('options.context')}`); - } else { + } else if (is.object(options.context)) { options.context = {...options.context}; + } else { + throw new TypeError(`Parameter \`context\` must be an object, not ${is('options.context')}`); } // `options.hooks` @@ -558,99 +643,16 @@ export class Request extends Duplex implements RequestEvents { options.decompress = Boolean(options.decompress); options.ignoreInvalidCookies = Boolean(options.ignoreInvalidCookies); options.followRedirects = Boolean(options.followRedirects); - options.maxRedirects = options.maxRedirects || 0; + options.maxRedirects = options.maxRedirects ?? 0; options.throwHttpErrors = Boolean(options.throwHttpErrors); options.http2 = Boolean(options.http2); options.allowGetBody = Boolean(options.allowGetBody); - return options; + return options as NormalizedOptions; } - constructor(url: string | URL, options?: Options, defaults?: Defaults) { - super(); - - this[kDownloadedSize] = 0; - this[kUploadedSize] = 0; - this.finalized = false; - this[kServerResponsesPiped] = new Set(); - this.redirects = []; - - if (!options) { - options = {}; - } - - this.on('pipe', source => { - if (source instanceof IncomingMessage) { - this.options.headers = { - ...source.headers, - ...this.options.headers - }; - } - }); - - (async (nonNormalizedOptions: Options) => { - try { - if (nonNormalizedOptions.body instanceof ReadStream) { - await waitForOpenFile(nonNormalizedOptions.body); - } - - const initHooks = nonNormalizedOptions.hooks?.init; - if (initHooks) { - nonNormalizedOptions = {...nonNormalizedOptions, url}; - - for (const hook of initHooks) { - await hook(nonNormalizedOptions as Options & {url: string | URL}); - } - } - - if (kIsNormalizedAlready in nonNormalizedOptions) { - this.options = nonNormalizedOptions as NormalizedOptions; - } else { - // @ts-ignore Common TypeScript bug saying that `this.constructor` is not accessible - this.options = this.constructor.normalizeArguments(url, nonNormalizedOptions, defaults); - } - - const {options} = this; - - this.requestUrl = options.url.toString(); - - await this.finalizeBody(); - - // Set cookies - if (options.cookieJar) { - const cookieString: string = await options.cookieJar.getCookieString(options.url.toString()); - - if (cookieString !== '') { - options.headers.cookie = cookieString; - } - } - - if (options.encoding) { - this.setEncoding(options.encoding); - } - - await this.makeRequest(); - - if (options.body instanceof Readable) { - options.body.pipe(this); - options.body.once('error', (error: NodeJS.ErrnoException) => { - this._beforeError(new UploadError(error, options, this)); - }); - } else if (options.body) { - this._writeRequest(options.body, null as unknown as string, () => {}); - this.end(); - } - - this.finalized = true; - this.emit('finalized'); - } catch (error) { - this._beforeError(error); - } - })(options); - } - - async finalizeBody() { - const options = this.options; + async finalizeBody(): Promise { + const {options} = this; const {headers} = options; const write = this.write.bind(this); @@ -682,13 +684,13 @@ export class Request extends Duplex implements RequestEvents { throw new TypeError('The `form` option must be an Object'); } - const lockWrite = () => { - this.write = () => { + const lockWrite = (): void => { + this.write = (): never => { throw new Error('The payload has been already provided'); }; }; - const dataListener = () => { + const dataListener = (): void => { this.write = write; }; @@ -756,13 +758,13 @@ export class Request extends Duplex implements RequestEvents { this[kBodySize] = Number(headers['content-length']) || undefined; } - async _onResponse(response: IncomingMessage) { + async _onResponse(response: IncomingMessage): Promise { const {options} = this; const {url} = options; const statusCode = response.statusCode!; const typedResponse = response as Response; - typedResponse.statusMessage = typedResponse.statusMessage !== '' ? typedResponse.statusMessage : http.STATUS_CODES[statusCode]; + typedResponse.statusMessage = typedResponse.statusMessage === '' ? http.STATUS_CODES[statusCode] : typedResponse.statusMessage; typedResponse.url = options.url.toString(); typedResponse.requestUrl = this.requestUrl; typedResponse.redirectUrls = this.redirects; @@ -773,6 +775,7 @@ export class Request extends Duplex implements RequestEvents { if (options.followRedirects && response.headers.location && redirectCodes.has(statusCode)) { response.resume(); // We're being redirected, we don't care about the response. + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete this[kRequest]; this[kUnproxyEvents](); @@ -800,6 +803,7 @@ export class Request extends Duplex implements RequestEvents { options.url = redirectUrl; for (const hook of options.hooks.beforeRedirect) { + // eslint-disable-next-line no-await-in-loop await hook(options, response); } @@ -868,6 +872,7 @@ export class Request extends Duplex implements RequestEvents { continue; } + // eslint-disable-next-line guard-for-in for (const key in response.headers) { const isAllowed = options.decompress ? key !== 'content-encoding' : true; const value = response.headers[key]; @@ -886,7 +891,7 @@ export class Request extends Duplex implements RequestEvents { this.emit('response', response); } - async makeRequest() { + async makeRequest(): Promise { if (kRequest in this) { return; } @@ -896,11 +901,13 @@ export class Request extends Duplex implements RequestEvents { for (const key in headers) { if (is.undefined(headers[key])) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete headers[key]; } } for (const hook of options.hooks.beforeRequest) { + // eslint-disable-next-line no-await-in-loop const result = await hook(options); if (result instanceof ResponseLike) { @@ -910,7 +917,7 @@ export class Request extends Duplex implements RequestEvents { } if (options.dnsCache) { - (options as any).lookup = (options.dnsCache as CacheableLookup).lookup; + (options as any).lookup = options.dnsCache.lookup; } // UNIX sockets @@ -948,7 +955,7 @@ export class Request extends Duplex implements RequestEvents { options[kRequest] = realFn as HttpRequestFunction; delete options.request; - let {timeout} = options; + const {timeout} = options; if (timeout) { delete options.timeout; } @@ -998,6 +1005,7 @@ export class Request extends Duplex implements RequestEvents { throw new Error('Fallback to `http.request` not implemented yet'); } else { // TODO: Rewrite `cacheable-request` + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion this._onResponse(request as unknown as IncomingMessage); } } catch (error) { @@ -1005,9 +1013,10 @@ export class Request extends Duplex implements RequestEvents { } } - async _beforeError(error: RequestError) { + async _beforeError(error: RequestError): Promise { try { for (const hook of this.options.hooks.beforeError) { + // eslint-disable-next-line no-await-in-loop error = await hook(error); } } catch (error_) { @@ -1017,7 +1026,7 @@ export class Request extends Duplex implements RequestEvents { this.destroy(error); } - _read() { + _read(): void { if (kResponse in this) { let data; @@ -1035,14 +1044,16 @@ export class Request extends Duplex implements RequestEvents { } } - _write(chunk: any, encoding: string, callback: (error?: Error | null) => void) { - const options = this.options; - if (options.method! in withoutBody) { + // A bug. + // eslint-disable-next-line @typescript-eslint/no-untyped-public-signature + _write(chunk: any, encoding: string, callback: (error?: Error | null) => void): void { + const {options} = this; + if (options.method in withoutBody) { callback(new TypeError(`The \`${options.method}\` method cannot be used with a body`)); return; } - const write = () => { + const write = (): void => { if (kRequest in this) { this._writeRequest(chunk, encoding, callback); } @@ -1055,7 +1066,9 @@ export class Request extends Duplex implements RequestEvents { } } - _writeRequest(chunk: any, encoding: string, callback: (error?: Error | null) => void) { + // A bug. + // eslint-disable-next-line @typescript-eslint/no-untyped-public-signature + _writeRequest(chunk: any, encoding: string, callback: (error?: Error | null) => void): void { this[kRequest].write(chunk, encoding, (error?: Error | null) => { if (!error) { this[kUploadedSize] += Buffer.byteLength(chunk, encoding as BufferEncoding); @@ -1071,8 +1084,8 @@ export class Request extends Duplex implements RequestEvents { }); } - _final(callback: (error?: Error | null) => void) { - const endRequest = () => { + _final(callback: (error?: Error | null) => void): void { + const endRequest = (): void => { // We need to check if `this[kRequest]` is present, // because it isn't when we use cache. if (!(kRequest in this)) { @@ -1098,11 +1111,11 @@ export class Request extends Duplex implements RequestEvents { } } - _destroy(error: Error | null, callback: (error: Error | null) => void) { + _destroy(error: Error | null, callback: (error: Error | null) => void): void { if (kRequest in this) { this[kRequest].abort(); } else { - this.once('finalized', () => { + this.once('finalized', (): void => { if (kRequest in this) { this[kRequest].abort(); } @@ -1116,11 +1129,11 @@ export class Request extends Duplex implements RequestEvents { callback(error); } - get socket() { + get socket(): Socket { return this[kRequest]?.socket; } - get aborted() { + get aborted(): boolean { return Boolean(this[kRequest]?.aborted); } @@ -1140,39 +1153,15 @@ export class Request extends Duplex implements RequestEvents { }; } - get timings() { + get timings(): Timings | undefined { return (this[kRequest] as ClientRequestWithTimings)?.timings; } - get flushedHeaders() { + get flushedHeaders(): boolean { return Boolean(this[kRequest]); } - static get(url: string | URL, options?: Options, defaults?: Defaults) { - return new this(url, {...options, method: 'GET'}, defaults); - }; - - static post(url: string | URL, options?: Options, defaults?: Defaults) { - return new this(url, {...options, method: 'POST'}, defaults); - }; - - static put(url: string | URL, options?: Options, defaults?: Defaults) { - return new this(url, {...options, method: 'PUT'}, defaults); - }; - - static patch(url: string | URL, options?: Options, defaults?: Defaults) { - return new this(url, {...options, method: 'PATCH'}, defaults); - }; - - static head(url: string | URL, options?: Options, defaults?: Defaults) { - return new this(url, {...options, method: 'HEAD'}, defaults); - }; - - static delete(url: string | URL, options?: Options, defaults?: Defaults) { - return new this(url, {...options, method: 'DELETE'}, defaults); - }; - - pipe(destination: T, options?: {end?: boolean;}): T { + pipe(destination: T, options?: {end?: boolean}): T { if (this.downloadProgress.transferred > 0) { throw new Error('Failed to pipe. The response has been emitted already.'); } @@ -1182,7 +1171,7 @@ export class Request extends Duplex implements RequestEvents { } return super.pipe(destination, options); - }; + } unpipe(destination: T): this { if (destination instanceof ServerResponse) { @@ -1192,9 +1181,5 @@ export class Request extends Duplex implements RequestEvents { super.unpipe(destination); return this; - }; + } } - -export default function request(url: string | URL, options?: Options, defaults?: Defaults) { - return new Request(url, options, defaults); -}; diff --git a/source/create.ts b/source/create.ts index d6fa9851a..3974cc8dd 100644 --- a/source/create.ts +++ b/source/create.ts @@ -11,8 +11,7 @@ import asPromise, { PaginationOptions } from './as-promise'; import createRejection from './as-promise/create-rejection'; -import asStream, { - Request, +import Request, { RequestError, CacheError, ReadError, @@ -40,7 +39,7 @@ const errors = { ParseError }; -const normalizeArguments = PromisableRequest.normalizeArguments; +const {normalizeArguments} = PromisableRequest; export type HTTPAlias = | 'get' @@ -53,7 +52,7 @@ export type HTTPAlias = export type GotReturn = Request | CancelableRequest; type GotStreamFunction = (url: string | URL, options?: Options & {isStream: true}) => Request; -const getPromiseOrStream = (options: NormalizedOptions): GotReturn => options.isStream ? asStream(options.url, options) : asPromise(options); +const getPromiseOrStream = (options: NormalizedOptions): GotReturn => options.isStream ? new Request(options.url, options) : asPromise(options); export type HandlerFunction = (options: NormalizedOptions, next: (options: NormalizedOptions) => T) => T | Promise; @@ -66,17 +65,23 @@ const isGotInstance = (value: Got | ExtendOptions): value is Got => ( 'defaults' in value && 'options' in value.defaults ); +type Except = Pick>; + export type OptionsOfTextResponseBody = Options & {isStream?: false; resolveBodyOnly?: false; responseType?: 'text'}; export type OptionsOfJSONResponseBody = Options & {isStream?: false; resolveBodyOnly?: false; responseType: 'json'}; export type OptionsOfBufferResponseBody = Options & {isStream?: false; resolveBodyOnly?: false; responseType: 'buffer'}; -export type GotStrictOptions = Omit; +export type GotStrictOptions = Except; type ResponseBodyOnly = {resolveBodyOnly: true}; export interface GotPaginate { (url: string | URL, options?: Options & PaginationOptions): AsyncIterableIterator; all(url: string | URL, options?: Options & PaginationOptions): Promise; + // A bug. + // eslint-disable-next-line @typescript-eslint/adjacent-overload-signatures (options?: Options & PaginationOptions): AsyncIterableIterator; + // A bug. + // eslint-disable-next-line @typescript-eslint/adjacent-overload-signatures all(options?: Options & PaginationOptions): Promise; } @@ -223,11 +228,15 @@ const create = (defaults: InstanceDefaults): Got => { const normalizedOptions = normalizeArguments(url, options, defaults.options); normalizedOptions[kIsNormalizedAlready] = true; + // A bug. + // eslint-disable-next-line @typescript-eslint/return-await return iterateHandlers(normalizedOptions); } catch (error) { if (options?.isStream) { throw error; } else { + // A bug. + // eslint-disable-next-line @typescript-eslint/return-await return createRejection(error); } } @@ -273,15 +282,17 @@ const create = (defaults: InstanceDefaults): Got => { const pagination = normalizedOptions._pagination!; if (typeof pagination !== 'object') { - throw new Error('`options._pagination` must be implemented'); + throw new TypeError('`options._pagination` must be implemented'); } const all: T[] = []; while (true) { // TODO: Throw when result is not an instance of Response - const result = await got('', normalizedOptions) as Response; + // eslint-disable-next-line no-await-in-loop + const result = (await got('', normalizedOptions)) as Response; + // eslint-disable-next-line no-await-in-loop const parsed = await pagination.transform(result); for (const item of parsed) { diff --git a/source/utils/proxy-events.ts b/source/utils/proxy-events.ts index eb50df110..bd78b4a64 100644 --- a/source/utils/proxy-events.ts +++ b/source/utils/proxy-events.ts @@ -19,4 +19,4 @@ export default function (from: EventEmitter, to: EventEmitter, events: string[]) from.off(event, fns[event]); } }; -}; +}